DotNext by .NET Foundation and Contributors

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

 ReaderWriterSpinLock

public struct ReaderWriterSpinLock
Represents lightweight reader-writer lock based on spin loop.
using System; using System.Diagnostics.CodeAnalysis; 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 uint version; private readonly bool valid; internal LockStamp([In] [IsReadOnly] ref uint version) { this.version = ref version.VolatileRead(); valid = true; } internal bool IsValid([In] [IsReadOnly] ref uint version) { if (valid) return this.version == 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([NotNullWhen(true)] 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 const int SingleReaderState = 1; private volatile int state; private uint 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) { if (stamp.IsValid(ref version)) return state != -2147483648; return false; } public bool TryEnterReadLock() { int num = state; int num2; do { num2 = num; if (num2 == -2147483648 || num2 == 2147483647) return false; } while ((num = Interlocked.CompareExchange(ref state, num2 + 1, num2)) != num2); return true; } public void EnterReadLock() { SpinWait spinWait = default(SpinWait); while (true) { int num = state; if (num != -2147483648 && num != 2147483647 && Interlocked.CompareExchange(ref state, num + 1, num) == num) break; spinWait.SpinOnce(); } } public void ExitReadLock() { Interlocked.Decrement(ref state); } private bool TryEnterReadLock([In] [IsReadOnly] ref Timeout timeout, CancellationToken token) { SpinWait spinWait = default(SpinWait); while (!timeout.IsExpired) { int num = state; if (num != -2147483648 && num != 2147483647 && Interlocked.CompareExchange(ref state, num + 1, num) == num) return true; token.ThrowIfCancellationRequested(); spinWait.SpinOnce(); } return false; } public bool TryEnterReadLock(TimeSpan timeout, CancellationToken token = default(CancellationToken)) { Timeout timeout2 = new Timeout(timeout); return TryEnterReadLock(ref timeout2, 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([In] [IsReadOnly] ref Timeout timeout, CancellationToken token) { SpinWait spinWait = default(SpinWait); while (Interlocked.CompareExchange(ref state, -2147483648, 0) != 0) { if (ref timeout) return false; token.ThrowIfCancellationRequested(); spinWait.SpinOnce(); } Interlocked.Increment(ref version); return true; } public bool TryEnterWriteLock(TimeSpan timeout, CancellationToken token = default(CancellationToken)) { Timeout timeout2 = new Timeout(timeout); return TryEnterWriteLock(ref timeout2, token); } public void ExitWriteLock() { state = 0; } public void UpgradeToWriteLock() { SpinWait spinWait = default(SpinWait); while (Interlocked.CompareExchange(ref state, -2147483648, 1) != 1) { spinWait.SpinOnce(); } Interlocked.Increment(ref version); } public bool TryUpgradeToWriteLock() { if (Interlocked.CompareExchange(ref state, -2147483648, 1) == 1) { Interlocked.Increment(ref version); return true; } return false; } private bool TryUpgradeToWriteLock([In] [IsReadOnly] ref Timeout timeout, CancellationToken token) { SpinWait spinWait = default(SpinWait); while (Interlocked.CompareExchange(ref state, -2147483648, 1) != 1) { if (ref timeout) return false; token.ThrowIfCancellationRequested(); spinWait.SpinOnce(); } Interlocked.Increment(ref version); return true; } public bool TryUpgradeToWriteLock(TimeSpan timeout, CancellationToken token = default(CancellationToken)) { Timeout timeout2 = new Timeout(timeout); return TryUpgradeToWriteLock(ref timeout2, token); } public void DowngradeFromWriteLock() { state = 1; } } }