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] [RequiresLocation] ref uint version)
{
this.version = Volatile.Read(ref version);
valid = true;
}
internal bool IsValid([In] [IsReadOnly] ref uint version)
{
if (valid)
return this.version == Volatile.Read(ref version);
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);
}
[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);
}
}
[IsReadOnly]
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) ? true : false)
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;
}
}
}