You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.9 KiB
170 lines
4.9 KiB
#if NET20 || NET30 || NET35 || !NET_4_6 |
|
|
|
using System.Diagnostics; |
|
using System.Runtime.ConstrainedExecution; |
|
using LinqInternal.Threading; |
|
|
|
namespace System.Threading |
|
{ |
|
[DebuggerDisplay("IsHeld = {IsHeld}")] |
|
public struct SpinLock |
|
{ |
|
private readonly bool _disableThreadTracking; |
|
private int _isHeld; |
|
private Thread _ownerThread; |
|
|
|
public SpinLock(bool enableThreadOwnerTracking) |
|
{ |
|
_disableThreadTracking = !enableThreadOwnerTracking; |
|
_ownerThread = null; |
|
_isHeld = 0; |
|
} |
|
|
|
public bool IsHeld |
|
{ |
|
get { return Thread.VolatileRead(ref _isHeld) == 1; } |
|
} |
|
|
|
public bool IsHeldByCurrentThread |
|
{ |
|
get |
|
{ |
|
if (_disableThreadTracking) |
|
{ |
|
throw new InvalidOperationException("Thread ownership tracking is disabled"); |
|
} |
|
else |
|
{ |
|
return IsHeld && ReferenceEquals(_ownerThread, Thread.CurrentThread); |
|
} |
|
} |
|
} |
|
|
|
public bool IsThreadOwnerTrackingEnabled |
|
{ |
|
get { return _disableThreadTracking; } |
|
} |
|
|
|
public void Enter(ref bool lockTaken) |
|
{ |
|
if (lockTaken) |
|
{ |
|
lockTaken = false; |
|
throw new ArgumentException(); |
|
} |
|
else |
|
{ |
|
if (_disableThreadTracking) |
|
{ |
|
var check = Interlocked.CompareExchange(ref _isHeld, 1, 0); |
|
if (check == 0) |
|
{ |
|
lockTaken = true; |
|
} |
|
else |
|
{ |
|
//Deadlock on recursion |
|
TryEnter(-1, ref lockTaken); |
|
} |
|
} |
|
else |
|
{ |
|
if (IsHeldByCurrentThread) |
|
{ |
|
//Throw on recursion |
|
throw new LockRecursionException(); |
|
} |
|
else |
|
{ |
|
if (Interlocked.CompareExchange(ref _isHeld, 1, 0) == 0 && ReferenceEquals(Interlocked.CompareExchange(ref _ownerThread, Thread.CurrentThread, null), null)) |
|
{ |
|
lockTaken = true; |
|
} |
|
else |
|
{ |
|
TryEnter(-1, ref lockTaken); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] |
|
public void Exit() |
|
{ |
|
Exit(true); |
|
} |
|
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] |
|
public void Exit(bool useMemoryBarrier) |
|
{ |
|
if (_disableThreadTracking) |
|
{ |
|
//Allow corruption: There is no check for what thread this is being called from |
|
ExitExtracted(useMemoryBarrier); |
|
} |
|
else |
|
{ |
|
if (IsHeldByCurrentThread) |
|
{ |
|
ExitExtracted(useMemoryBarrier); |
|
} |
|
else |
|
{ |
|
throw new SynchronizationLockException(); |
|
} |
|
} |
|
} |
|
|
|
public void TryEnter(ref bool lockTaken) |
|
{ |
|
if (lockTaken) |
|
{ |
|
lockTaken = false; |
|
throw new ArgumentException(); |
|
} |
|
TryEnter(0, ref lockTaken); |
|
} |
|
|
|
public void TryEnter(TimeSpan timeout, ref bool lockTaken) |
|
{ |
|
TryEnter((int)timeout.TotalMilliseconds, ref lockTaken); |
|
} |
|
|
|
public void TryEnter(int millisecondsTimeout, ref bool lockTaken) |
|
{ |
|
if (_disableThreadTracking) |
|
{ |
|
lockTaken |= ThreadingHelper.SpinWaitSet(ref _isHeld, 1, 0, millisecondsTimeout); |
|
} |
|
else |
|
{ |
|
if (IsHeldByCurrentThread) |
|
{ |
|
//Throw on recursion |
|
throw new LockRecursionException(); |
|
} |
|
else |
|
{ |
|
lockTaken |= (ThreadingHelper.SpinWaitSet(ref _isHeld, 1, 0, millisecondsTimeout) && ReferenceEquals(Interlocked.CompareExchange(ref _ownerThread, Thread.CurrentThread, null), null)); |
|
} |
|
} |
|
} |
|
|
|
private void ExitExtracted(bool useMemoryBarrier) |
|
{ |
|
if (useMemoryBarrier) |
|
{ |
|
Thread.VolatileWrite(ref _isHeld, 0); |
|
Volatile.Write(ref _ownerThread, null); |
|
} |
|
else |
|
{ |
|
_isHeld = 0; |
|
_ownerThread = null; |
|
} |
|
} |
|
} |
|
} |
|
|
|
#endif |