ResourceLoader: Properly push & pop TLS state on recursive load tasks

This commit is contained in:
Pedro J. Estébanez 2024-07-10 12:01:22 +02:00
parent 5ca419e32c
commit bd0959ebdd
2 changed files with 24 additions and 0 deletions

View File

@ -207,6 +207,24 @@ void ResourceFormatLoader::_bind_methods() {
/////////////////////////////////// ///////////////////////////////////
// These are used before and after a wait for a WorkerThreadPool task
// because that can lead to another load started in the same thread,
// something we must treat as a different stack for the purposes
// of tracking nesting.
#define PREPARE_FOR_WTP_WAIT \
int load_nesting_backup = ResourceLoader::load_nesting; \
Vector<String> load_paths_stack_backup = ResourceLoader::load_paths_stack; \
ResourceLoader::load_nesting = 0; \
ResourceLoader::load_paths_stack.clear();
#define RESTORE_AFTER_WTP_WAIT \
DEV_ASSERT(ResourceLoader::load_nesting == 0); \
DEV_ASSERT(ResourceLoader::load_paths_stack.is_empty()); \
ResourceLoader::load_nesting = load_nesting_backup; \
ResourceLoader::load_paths_stack = load_paths_stack_backup; \
load_paths_stack_backup.clear();
// This should be robust enough to be called redundantly without issues. // This should be robust enough to be called redundantly without issues.
void ResourceLoader::LoadToken::clear() { void ResourceLoader::LoadToken::clear() {
thread_load_mutex.lock(); thread_load_mutex.lock();
@ -234,7 +252,9 @@ void ResourceLoader::LoadToken::clear() {
// If task is unused, await it here, locally, now the token data is consistent. // If task is unused, await it here, locally, now the token data is consistent.
if (task_to_await) { if (task_to_await) {
PREPARE_FOR_WTP_WAIT
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_to_await); WorkerThreadPool::get_singleton()->wait_for_task_completion(task_to_await);
RESTORE_AFTER_WTP_WAIT
} }
} }
@ -698,7 +718,9 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
// Loading thread is in the worker pool. // Loading thread is in the worker pool.
load_task.awaited = true; load_task.awaited = true;
thread_load_mutex.unlock(); thread_load_mutex.unlock();
PREPARE_FOR_WTP_WAIT
wtp_task_err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id); wtp_task_err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
RESTORE_AFTER_WTP_WAIT
} }
if (load_task.status == THREAD_LOAD_IN_PROGRESS) { // If early errored, awaiting would deadlock. if (load_task.status == THREAD_LOAD_IN_PROGRESS) { // If early errored, awaiting would deadlock.

View File

@ -100,6 +100,8 @@ typedef Error (*ResourceLoaderImport)(const String &p_path);
typedef void (*ResourceLoadedCallback)(Ref<Resource> p_resource, const String &p_path); typedef void (*ResourceLoadedCallback)(Ref<Resource> p_resource, const String &p_path);
class ResourceLoader { class ResourceLoader {
friend class LoadToken;
enum { enum {
MAX_LOADERS = 64 MAX_LOADERS = 64
}; };