Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to see and unload Resources loaded by ResourceLoader.load_threaded_request() #86603

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

AlexOtsuka
Copy link
Contributor

In response to my own needs and inspired by this proposal, I've added 2 new methods to ResourceLoader:

  • ResourceLoader.load_threaded_cancel() is the direct counterpart to ResourceLoader.load_threaded_request()
  • ResourceLoader.get_requested_paths() returns an Array listing all currently requested Resources

Without reiterating the aforementioned thread, I believe those are core features needed to properly manage background loading of Resources in many different contexts. This will let users track and cancel Resources they haven't ended up needing and will work out of the box in existing projects.

Looking forward to your feedback and criticism.

Comment on lines 452 to 456
thread_load_mutex.lock();
for (KeyValue<String, ResourceLoader::LoadToken *> &kvp : user_load_tokens) {
requested_paths.push_back(kvp.key);
}
thread_load_mutex.unlock();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
thread_load_mutex.lock();
for (KeyValue<String, ResourceLoader::LoadToken *> &kvp : user_load_tokens) {
requested_paths.push_back(kvp.key);
}
thread_load_mutex.unlock();
MutexLock lock(thread_load_mutex);
for (KeyValue<String, ResourceLoader::LoadToken *> &kvp : user_load_tokens) {
requested_paths.push_back(kvp.key);
}

Safer and preferred usage with trivial locking

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for reviewing my PR, I've applied your suggestion!

@AThousandShips
Copy link
Member

Welcome to contributing! Will take a deeper dive into this when I have the time 🙂

@AlexOtsuka AlexOtsuka force-pushed the cancellable-threaded-loading-requests branch 2 times, most recently from b1cde5b to 6342624 Compare December 29, 2023 14:23
@@ -420,6 +420,44 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
}
}

void ResourceLoader::load_threaded_cancel(const String &p_path) {
Copy link
Member

@RandomShaper RandomShaper Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm understanding correctly, this is a load_threaded_get() without honoring reference counting. In that case, I'd suggest refactoring the code so both functions share a common implementation. There would be a new argument to ignore the ref-count and instead relentessly clear it. Moreover, since it's not actually a proper cancel, I'd name this one load_threaded_forget(). But this leads to the question, wouldn't it be even cooler to have a proper load_threaded_cancel() that actually stops the load? Nonetheless, that would be much harder to implement and could be an addition for the future. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello RandomShaper,

Thank you for your feedback. I have read your post and agree with pretty much the entirety of your thoughts. I'm sick right now, but I will get back into this and give a more comprehensive answer.

Refactoring the code is something I had been avoiding as not to break previously existing code, but is obviously worth considering if such a change is positively welcomed by the community.

When it comes to cancelling the actual loading process from load_threaded_request(), I will have to dive deeper into its current implementation and figure out how to do this in a thread-safe manner.

Will spend more time on this when my brain works again.

@AlexOtsuka AlexOtsuka force-pushed the cancellable-threaded-loading-requests branch 2 times, most recently from d8238f0 to d262e83 Compare January 17, 2024 04:38
@AlexOtsuka AlexOtsuka force-pushed the cancellable-threaded-loading-requests branch from d262e83 to 8e4d560 Compare January 17, 2024 04:50
@AlexOtsuka
Copy link
Contributor Author

AlexOtsuka commented Jan 17, 2024

I've made a few changes following feedback from @RandomShaper and after acquiring a deeper understanding of ResourceLoader.

  • loaded_threaded_cancel() had been renamed to loaded_threaded_forget() as suggested. Especially with the changes I've now made to it, it is a much more appropriate name for the method.
  • loaded_threaded_forget() is now non-blocking. Instead of waiting to unload resources that are currently being requested, it only deals with resources that have already fully finished loading.
  • Because of this change, get_requested_paths() now only returns paths to resources that have finished loading after being requested, as in resources that are ready to be instantly disposed of by calling load_threaded_forget().

I have tried implemented a form of load_threaded_cancel() acting on currently loading resources while being non-blocking at the same time. It basically revolved around adding a task to WorkerThreadPool and waiting for the loading to finish from inside another thread, and while it worked reasonably well, I couldn't find a way to prevent it from crashing when calling load_threaded_get() on that same Resource before that task was completed. While this is definitely implementable, it'd require modifying other parts of resource_loader.cpp, and the functionality doesn't seem worthwhile as it would still not be capable of immediately aborting load_threaded_request(). Which as far as I can tell, cannot easily be done.

Because load_threaded_forget() has been made so much simpler, refactoring the code doesn't seem necessary anymore. load_threaded_get() goes through a number of checks that are unnecessary in load_threaded_forget() (we don't care if the resource is valid) and _load_complete() is already being used by both threaded and non-threaded loading methods.

I hadn't actually realized before working on this that load_threaded_request() increased the reference count for the LoadToken every time it was called on the same resource (I assumed the call was simply ignored). This actually increases the need for load_threaded_forget(), as there's no clear way to keep track of how many calls to load_threaded_request() were previously made and how many calls to load_threaded_get() would be necessary in order to flush the resource out of the cache.

Thank you for your interest so far, and please inform me of any feedback or criticism you may have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants