From 54adc5f9fb65f4b03bc28da5899d2413a84f66c2 Mon Sep 17 00:00:00 2001
From: riperiperi <rhy3756547@hotmail.com>
Date: Sun, 29 Aug 2021 20:03:41 +0100
Subject: 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
---
 Ryujinx.Memory/Tracking/RegionHandle.cs | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

(limited to 'Ryujinx.Memory/Tracking/RegionHandle.cs')

diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs
index 2df02f1e..40deba98 100644
--- a/Ryujinx.Memory/Tracking/RegionHandle.cs
+++ b/Ryujinx.Memory/Tracking/RegionHandle.cs
@@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
 
         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 readonly List<VirtualRegion> _regions;
         private readonly MemoryTracking _tracking;
@@ -115,17 +116,16 @@ namespace Ryujinx.Memory.Tracking
         /// <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)
         {
-            RegionSignal action = Interlocked.Exchange(ref _preAction, null);
-
             // If this handle was already unmapped (even if just partially),
             // then we have nothing to do until it is mapped again.
             // The pre-action should be still consumed to avoid flushing on remap.
             if (Unmapped)
             {
+                Interlocked.Exchange(ref _preAction, null);
                 return;
             }
 
-            if (action != null)
+            if (_preAction != null)
             {
                 // Copy the handles list in case it changes when we're out of the lock.
                 if (handleIterable is List<RegionHandle>)
@@ -138,7 +138,12 @@ namespace Ryujinx.Memory.Tracking
 
                 try
                 {
-                    action.Invoke(address, size);
+                    lock (_preActionLock)
+                    {
+                        _preAction?.Invoke(address, size);
+
+                        _preAction = null;
+                    }
                 }
                 finally
                 {
@@ -220,14 +225,19 @@ namespace Ryujinx.Memory.Tracking
         {
             ClearVolatile();
 
-            RegionSignal lastAction = Interlocked.Exchange(ref _preAction, action);
-            if (lastAction == null && action != lastAction)
+            lock (_preActionLock)
             {
-                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();
+                        }
                     }
                 }
             }
-- 
cgit v1.2.3-70-g09d2