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([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 Unsafe.AsRef(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(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 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)
return false;
} while (Interlocked.CompareExchange(ref state, checked(num + 1), num) != num);
return true;
}
public void EnterReadLock()
{
SpinWait spinWait = default(SpinWait);
while (true) {
int num = state;
if (num == -2147483648)
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)
{
SpinWait spinWait = default(SpinWait);
while (!timeout.IsExpired) {
int num;
if ((num = state) == -2147483648)
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()
{
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(Timeout timeout, CancellationToken token)
{
SpinWait spinWait = default(SpinWait);
while (Interlocked.CompareExchange(ref state, -2147483648, 0) != 0) {
if (timeout.IsExpired)
return false;
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;
}
}
}