Fix NCE Trapping API Deadlock

A deadlock was caused by holding `trapMutex` while waiting on the lock of a resource inside a callback while another thread holding the resource's mutex waits on `trapMutex`. This has been fixed by no longer allowing blocking locks inside the callbacks and introducing a separate callback for locking the resource which is done after unlocking the `trapMutex` which can then be locked by any contending threads.
This commit is contained in:
PixelyIon
2022-07-02 21:45:50 +05:30
parent a6599c30b4
commit 3ca56ef578
4 changed files with 77 additions and 47 deletions

View File

@ -145,17 +145,23 @@ namespace skyline::gpu {
trapHandle = gpu.state.nce->TrapRegions(mappings, true, [this] {
std::scoped_lock lock{*this};
}, [this] {
std::unique_lock lock{*this, std::try_to_lock};
if (!lock)
return false;
SynchronizeGuest(true); // We can skip trapping since the caller will do it
WaitOnFence();
return true;
}, [this] {
DirtyState expectedState{DirtyState::Clean};
if (dirtyState.compare_exchange_strong(expectedState, DirtyState::CpuDirty, std::memory_order_relaxed) || expectedState == DirtyState::CpuDirty)
return; // If we can transition the texture to CPU dirty (from Clean) or if it already is CPU dirty then we can just return, we only need to do the lock and corresponding sync if the texture is GPU dirty
return true; // If we can transition the texture to CPU dirty (from Clean) or if it already is CPU dirty then we can just return, we only need to do the lock and corresponding sync if the texture is GPU dirty
std::scoped_lock lock{*this};
std::unique_lock lock{*this, std::try_to_lock};
if (!lock)
return false;
SynchronizeGuest(true);
dirtyState = DirtyState::CpuDirty; // We need to assume the texture is dirty since we don't know what the guest is writing
WaitOnFence();
return true;
});
}