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;
}
}
}