/ src / Ryujinx.Common / Memory / PartialUnmaps / NativeReaderWriterLock.cs
NativeReaderWriterLock.cs
 1  using System.Runtime.InteropServices;
 2  using System.Threading;
 3  using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers;
 4  
 5  namespace Ryujinx.Common.Memory.PartialUnmaps
 6  {
 7      /// <summary>
 8      /// A simple implementation of a ReaderWriterLock which can be used from native code.
 9      /// </summary>
10      [StructLayout(LayoutKind.Sequential, Pack = 4)]
11      public struct NativeReaderWriterLock
12      {
13          public int WriteLock;
14          public int ReaderCount;
15  
16          public static readonly int WriteLockOffset;
17          public static readonly int ReaderCountOffset;
18  
19          /// <summary>
20          /// Populates the field offsets for use when emitting native code.
21          /// </summary>
22          static NativeReaderWriterLock()
23          {
24              NativeReaderWriterLock instance = new();
25  
26              WriteLockOffset = OffsetOf(ref instance, ref instance.WriteLock);
27              ReaderCountOffset = OffsetOf(ref instance, ref instance.ReaderCount);
28          }
29  
30          /// <summary>
31          /// Acquires the reader lock.
32          /// </summary>
33          public void AcquireReaderLock()
34          {
35              // Must take write lock for a very short time to become a reader.
36  
37              while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0)
38              {
39              }
40  
41              Interlocked.Increment(ref ReaderCount);
42  
43              Interlocked.Exchange(ref WriteLock, 0);
44          }
45  
46          /// <summary>
47          /// Releases the reader lock.
48          /// </summary>
49          public void ReleaseReaderLock()
50          {
51              Interlocked.Decrement(ref ReaderCount);
52          }
53  
54          /// <summary>
55          /// Upgrades to a writer lock. The reader lock is temporarily released while obtaining the writer lock.
56          /// </summary>
57          public void UpgradeToWriterLock()
58          {
59              // Prevent any more threads from entering reader.
60              // If the write lock is already taken, wait for it to not be taken.
61  
62              Interlocked.Decrement(ref ReaderCount);
63  
64              while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0)
65              {
66              }
67  
68              // Wait for reader count to drop to 0, then take the lock again as the only reader.
69  
70              while (Interlocked.CompareExchange(ref ReaderCount, 1, 0) != 0)
71              {
72              }
73          }
74  
75          /// <summary>
76          /// Downgrades from a writer lock, back to a reader one.
77          /// </summary>
78          public void DowngradeFromWriterLock()
79          {
80              // Release the WriteLock.
81  
82              Interlocked.Exchange(ref WriteLock, 0);
83          }
84      }
85  }