DotNext by .NET Foundation and Contributors

<PackageReference Include="DotNext" Version="3.3.0" />

 ReaderWriterSpinLock

public struct ReaderWriterSpinLock
Represents lightweight reader-writer lock based on spin loop.
using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; namespace DotNext.Threading { [StructLayout(LayoutKind.Auto)] public struct ReaderWriterSpinLock { [StructLayout(LayoutKind.Auto)] public readonly struct LockStamp : IEquatable<LockStamp> { private readonly int version; private readonly bool valid; internal LockStamp([In] [IsReadOnly] ref int version) { this.version = ref version.VolatileRead(); valid = true; } internal bool IsValid([In] [IsReadOnly] ref int version) { if (valid) return this.version == ref Unsafe.AsRef(ref version).VolatileRead(); return false; } private bool Equals([In] [IsReadOnly] ref LockStamp other) { if (version == other.version) return valid == other.valid; return false; } public bool Equals(LockStamp other) { return Equals(ref other); } [System.Runtime.CompilerServices.NullableContext(2)] public override bool Equals(object other) { if (other is LockStamp) { LockStamp other2 = (LockStamp)other; return Equals(ref other2); } return false; } public override int GetHashCode() { return HashCode.Combine(valid, version); } public static bool operator ==([In] [IsReadOnly] ref LockStamp first, [In] [IsReadOnly] ref LockStamp second) { return first.Equals(ref second); } public static bool operator !=([In] [IsReadOnly] ref LockStamp first, [In] [IsReadOnly] ref LockStamp second) { return !first.Equals(ref second); } } private const int WriteLockState = int.MinValue; private const int NoLockState = 0; private volatile int state; private int version; public bool IsWriteLockHeld { [IsReadOnly] get { return state == -2147483648; } } public bool IsReadLockHeld { [IsReadOnly] get { return state > 0; } } public int CurrentReadCount { [IsReadOnly] get { return Math.Max(0, state); } } public LockStamp TryOptimisticRead() { LockStamp result = new LockStamp(ref version); if (state != -2147483648) return result; return default(LockStamp); } [IsReadOnly] public bool Validate([In] [IsReadOnly] ref LockStamp stamp) { return stamp.IsValid(ref version); } public bool TryEnterReadLock() { int num; do { num = state; if (num == -2147483648) return false; } while (Interlocked.CompareExchange(ref state, checked(num + 1), num) != num); return true; } public void EnterReadLock() { SpinWait spinWait = default(SpinWait); while (true) { int num = state; if (num == -2147483648) spinWait.SpinOnce(); else if (Interlocked.CompareExchange(ref state, checked(num + 1), num) == num) { break; } } } public void ExitReadLock() { Interlocked.Decrement(ref state); } private bool TryEnterReadLock(Timeout timeout, CancellationToken token) { SpinWait spinWait = default(SpinWait); while (!timeout.IsExpired) { int num; if ((num = state) == -2147483648) spinWait.SpinOnce(); else if (Interlocked.CompareExchange(ref state, checked(num + 1), num) == num) { return true; } token.ThrowIfCancellationRequested(); } return false; } public bool TryEnterReadLock(TimeSpan timeout, CancellationToken token = default(CancellationToken)) { return TryEnterReadLock(new Timeout(timeout), token); } public void EnterWriteLock() { SpinWait spinWait = default(SpinWait); while (Interlocked.CompareExchange(ref state, -2147483648, 0) != 0) { spinWait.SpinOnce(); } Interlocked.Increment(ref version); } public bool TryEnterWriteLock() { if (Interlocked.CompareExchange(ref state, -2147483648, 0) == 0) { Interlocked.Increment(ref version); return true; } return false; } private bool TryEnterWriteLock(Timeout timeout, CancellationToken token) { SpinWait spinWait = default(SpinWait); while (Interlocked.CompareExchange(ref state, -2147483648, 0) != 0) { if (timeout.IsExpired) return false; spinWait.SpinOnce(); token.ThrowIfCancellationRequested(); } Interlocked.Increment(ref version); return true; } public bool TryEnterWriteLock(TimeSpan timeout, CancellationToken token = default(CancellationToken)) { return TryEnterWriteLock(new Timeout(timeout), token); } public void ExitWriteLock() { state = 0; } } }