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 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 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 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 || num == 2147483647)
return false;
} while (Interlocked.CompareExchange(ref state, num + 1, num) != num);
return true;
}
public void EnterReadLock()
{
SpinWait spinWait = default(SpinWait);
while (true) {
int num = state;
if (num != -2147483648 && 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 && 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;
}
}
}