DotNext by Roman Sakno

<PackageReference Include="DotNext" Version="1.2.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(ref int version) { this.version = ref version.VolatileRead(); valid = true; } internal bool IsValid(ref int version) { if (valid) return this.version == ref version.VolatileRead(); return false; } public bool Equals(LockStamp other) { if (version == other.version) return valid == other.valid; return false; } public override bool Equals(object other) { if (other is LockStamp) { LockStamp other2 = (LockStamp)other; return Equals(other2); } return false; } public override int GetHashCode() { return version; } public static bool operator ==([In] [System.Runtime.CompilerServices.IsReadOnly] ref LockStamp first, [In] [System.Runtime.CompilerServices.IsReadOnly] ref LockStamp second) { if (first.version == second.version) return first.valid == second.valid; return false; } public static bool operator !=([In] [System.Runtime.CompilerServices.IsReadOnly] ref LockStamp first, [In] [System.Runtime.CompilerServices.IsReadOnly] ref LockStamp second) { if (first.version == second.version) return first.valid ^ second.valid; return true; } } private const int WriteLockState = int.MinValue; private const int NoLockState = 0; private volatile int state; private int version; public bool IsWriteLockHeld => state == -2147483648; public bool IsReadLockHeld => state > 0; public int CurrentReadCount => Math.Max(0, state); public LockStamp TryOptimisticRead() { LockStamp result = new LockStamp(ref version); if (state != -2147483648) return result; return default(LockStamp); } public bool Validate([In] [System.Runtime.CompilerServices.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() { while (true) { int num = state; if (num == -2147483648) { SpinWait spinWait = default(SpinWait); 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) { while (!timeout.IsExpired) { int num; if ((num = state) == -2147483648) { SpinWait spinWait = default(SpinWait); 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() { while (Interlocked.CompareExchange(ref state, -2147483648, 0) != 0) { SpinWait spinWait = default(SpinWait); spinWait.SpinOnce(); } Interlocked.Increment(ref version); } public bool TryEnterWriteLock() { bool num = Interlocked.CompareExchange(ref state, -2147483648, 0) == 0; if (num) Interlocked.Increment(ref version); return num; } private bool TryEnterWriteLock(Timeout timeout, CancellationToken token) { while (Interlocked.CompareExchange(ref state, -2147483648, 0) != 0) { if (timeout.IsExpired) return false; SpinWait spinWait = default(SpinWait); 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; } } }