Ensure that all threads wait for a read tracking action to complete. (#2597)

* Lock around tracking action consume + execute. Not particularly fast.

* Lock around preaction registration and use

* Create a lock object

* Nit
This commit is contained in:
riperiperi 2021-08-29 20:03:41 +01:00 committed by GitHub
parent 76e8f9ac87
commit 54adc5f9fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
private event Action _onDirty; private event Action _onDirty;
private object _preActionLock = new object();
private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access. private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access.
private readonly List<VirtualRegion> _regions; private readonly List<VirtualRegion> _regions;
private readonly MemoryTracking _tracking; private readonly MemoryTracking _tracking;
@ -115,17 +116,16 @@ namespace Ryujinx.Memory.Tracking
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
internal void Signal(ulong address, ulong size, bool write, ref IList<RegionHandle> handleIterable) internal void Signal(ulong address, ulong size, bool write, ref IList<RegionHandle> handleIterable)
{ {
RegionSignal action = Interlocked.Exchange(ref _preAction, null);
// If this handle was already unmapped (even if just partially), // If this handle was already unmapped (even if just partially),
// then we have nothing to do until it is mapped again. // then we have nothing to do until it is mapped again.
// The pre-action should be still consumed to avoid flushing on remap. // The pre-action should be still consumed to avoid flushing on remap.
if (Unmapped) if (Unmapped)
{ {
Interlocked.Exchange(ref _preAction, null);
return; return;
} }
if (action != null) if (_preAction != null)
{ {
// Copy the handles list in case it changes when we're out of the lock. // Copy the handles list in case it changes when we're out of the lock.
if (handleIterable is List<RegionHandle>) if (handleIterable is List<RegionHandle>)
@ -138,7 +138,12 @@ namespace Ryujinx.Memory.Tracking
try try
{ {
action.Invoke(address, size); lock (_preActionLock)
{
_preAction?.Invoke(address, size);
_preAction = null;
}
} }
finally finally
{ {
@ -220,14 +225,19 @@ namespace Ryujinx.Memory.Tracking
{ {
ClearVolatile(); ClearVolatile();
RegionSignal lastAction = Interlocked.Exchange(ref _preAction, action); lock (_preActionLock)
if (lastAction == null && action != lastAction)
{ {
lock (_tracking.TrackingLock) RegionSignal lastAction = _preAction;
_preAction = action;
if (lastAction == null && action != lastAction)
{ {
foreach (VirtualRegion region in _regions) lock (_tracking.TrackingLock)
{ {
region.UpdateProtection(); foreach (VirtualRegion region in _regions)
{
region.UpdateProtection();
}
} }
} }
} }