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

VideoCommon: add additional locks around asset access / usage #11891

Merged
merged 1 commit into from Jun 5, 2023

Conversation

iwubcode
Copy link
Contributor

@iwubcode iwubcode commented Jun 5, 2023

For the most part, this review adds additional locks in place for thread safety. There were a few places where locks were being held onto too long, that has been updated as well.

iter != m_assetid_to_asset_map_path.end())
{
const auto& asset_map_path = iter->second;
const auto asset_map = GetAssetMapForID(asset_id);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I chose to prefer a copy instead of holding onto the lock for the whole time that the texture was loaded

@@ -50,14 +50,20 @@ class CustomAssetLoader
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<AssetType>>& asset_map,
std::shared_ptr<CustomAssetLibrary> library)
{
std::lock_guard lk(m_asset_map_lock);
auto [it, inserted] = asset_map.try_emplace(asset_id);
if (!inserted)
return it->second.lock();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm just noticing this now, but does this actually work correctly for expired weak_ptrs? The weak_ptr doesn't cease to exist when the pointed at shared_ptr no longer exists, it just returns an empty shared_ptr on lock, right? So I would actually expect something like:

    if (!inserted)
    {
      auto shared = it->second.lock();
      if (shared)
        return shared;
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess that's what the custom deleter is for, now that I think about it a bit more... but is that completely threadsafe? What if one thread is in the deleter waiting for the m_asset_map_lock while another thread is currently in the LoadOrCreateAsset() function? Can that even happen...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When the last shared_ptr gets destroyed, it will clear out the entry from the map, so in theory it should never hit the case of nullptr but it's better to be safe. Ok if I add it to this review?

Copy link
Contributor Author

@iwubcode iwubcode Jun 5, 2023

Choose a reason for hiding this comment

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

Missed your other comment. That's a good point, does seem like it could be racey (seems very unlikely). You'd have to be in the deleter while also adding to that same asset-id

Copy link
Contributor

Choose a reason for hiding this comment

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

Does the shared_ptr count as deleted by the time the deleter is called?? I think I need to debug through this with a test case, this is a very odd construct you've built here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively, maybe we just accept that dead weak_ptrs can hang around in the map and remove the map-erase in the deleter, that seems a lot more straightforward.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this approach is a bit of a double edged sword. Nice from a usability perspective but does have some concerns to think through. You were right to call this out. I'm not seeing anything in the std that talks about this. Will have to look a little closer in a little while.

Copy link
Contributor Author

@iwubcode iwubcode Jun 5, 2023

Choose a reason for hiding this comment

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

Alternatively, maybe we just accept that dead weak_ptrs can hang around in the map and remove the map-erase in the deleter, that seems a lot more straightforward.

Yeah, I suppose there aren't going to be that many assets floating around. Maybe I was trying to be too clever..

Copy link
Contributor Author

@iwubcode iwubcode Jun 5, 2023

Choose a reason for hiding this comment

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

I decided to remove the map erase for now. I'm going to investigate this on the side to see if we can still have the auto delete

@AdmiralCurtiss AdmiralCurtiss merged commit 678db26 into dolphin-emu:master Jun 5, 2023
14 checks passed
@iwubcode iwubcode deleted the asset_thread_safety branch June 5, 2023 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants