网上演练
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.

419 lines
13 KiB

#if NET20 || NET30 || NET35 || !NET_4_6
using LinqInternal.Threading;
namespace System.Threading
{
public class ManualResetEventSlim : IDisposable
{
private const int _defaultSpinCount = 10;
private readonly int _spinCount;
private ManualResetEvent _handle;
// _requested: -1 = Disposed, 0 = Not requested, 1 = Requested, 2 = _handle ready
private int _requested;
// _state: -1 = Disposed, 0 = Not Set, 1 = Set -- Do not set to 0 or 1 when _requested == 1
private int _state;
public ManualResetEventSlim()
: this(false)
{
//Empty
}
public ManualResetEventSlim(bool initialState)
{
_state = initialState ? 1 : 0;
_spinCount = _defaultSpinCount;
}
public ManualResetEventSlim(bool initialState, int spinCount)
{
if (spinCount < 0 || spinCount > 2047)
{
throw new ArgumentOutOfRangeException("spinCount");
}
_spinCount = spinCount;
_state = initialState ? 1 : 0;
}
public bool IsSet
{
get
{
// The value returned by this property should be considered out of sync
if (Thread.VolatileRead(ref _state) == -1)
{
return false;
}
var handle = GetWaitHandle();
if (handle != null)
{
return handle.WaitOne(0);
}
return Thread.VolatileRead(ref _state) != 0;
}
}
public int SpinCount
{
get { return _spinCount; }
}
public WaitHandle WaitHandle
{
get
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
return RetriveWaitHandle();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Reset()
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
var handle = GetWaitHandle();
if (handle != null)
{
handle.Reset();
}
Thread.VolatileWrite(ref _state, 0);
}
public void Set()
{
if (Thread.VolatileRead(ref _state) == -1)
{
// Silent fail
}
else
{
try
{
var handle = GetWaitHandle();
if (handle != null)
{
handle.Set();
}
if (Interlocked.CompareExchange(ref _state, 1, 0) == 0)
{
handle = GetWaitHandle();
if (handle != null)
{
handle.Set();
}
}
}
catch (ObjectDisposedException ex)
{
GC.KeepAlive(ex);
// Silent fail
}
}
}
public void Wait()
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
var spinWait = new SpinWait();
var spinCount = _spinCount;
if (!IsSet)
{
retry:
if (!IsSet)
{
if (spinCount > 0)
{
spinCount--;
spinWait.SpinOnce();
goto retry;
}
var handle = RetriveWaitHandle();
handle.WaitOne();
}
}
}
public bool Wait(int millisecondsTimeout)
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (millisecondsTimeout < -1)
{
throw new ArgumentOutOfRangeException("millisecondsTimeout");
}
if (millisecondsTimeout == -1)
{
Wait();
return true;
}
return WaitExtracted(millisecondsTimeout);
}
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
if (millisecondsTimeout < -1)
{
throw new ArgumentOutOfRangeException("millisecondsTimeout");
}
if (millisecondsTimeout == -1)
{
WaitExtracted(cancellationToken);
return true;
}
return WaitExtracted(millisecondsTimeout, cancellationToken);
}
public void Wait(CancellationToken cancellationToken)
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
WaitExtracted(cancellationToken);
}
public bool Wait(TimeSpan timeout)
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
var milliseconds = timeout.TotalMilliseconds;
return WaitExtracted((int)milliseconds);
}
public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
{
if (Thread.VolatileRead(ref _state) == -1)
{
throw new ObjectDisposedException(GetType().FullName);
}
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
var milliseconds = timeout.TotalMilliseconds;
return WaitExtracted((int)milliseconds, cancellationToken);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (Interlocked.Exchange(ref _state, -1) != -1)
{
Thread.VolatileWrite(ref _requested, -1);
var handle = Interlocked.Exchange(ref _handle, null);
if (handle != null)
{
handle.Close();
}
}
}
}
private ManualResetEvent GetWaitHandle()
{
var found = Thread.VolatileRead(ref _requested);
switch (found)
{
case -1:
throw new ObjectDisposedException(GetType().FullName);
case 0:
return null;
case 1:
// Found 1, another thread is creating the wait handle
ThreadingHelper.SpinWaitUntil(ref _requested, 2);
goto default;
default:
// Found 2, the wait handle is already created
// Check if dispose has been called
return TryGetWaitHandleExtracted();
}
}
private ManualResetEvent RetriveWaitHandle()
{
// At the end of this method: _requested will be 2 or ObjectDisposedException is thrown
var found = Interlocked.CompareExchange(ref _requested, 1, 0);
switch (found)
{
case -1:
throw new ObjectDisposedException(GetType().FullName);
case 0:
// Found 0, was set to 1, create the wait handle
var isSet = Thread.VolatileRead(ref _state) != 0;
// State may have been set here
var created = new ManualResetEvent(isSet);
if (Interlocked.CompareExchange(ref _handle, created, null) != null)
{
created.Close();
}
Thread.VolatileWrite(ref _requested, 2);
goto default;
case 1:
// Found 1, another thread is creating the wait handle
ThreadingHelper.SpinWaitUntil(ref _requested, 2);
goto default;
default:
// Found 2, the wait handle is already created
// Check if dispose has been called
return TryGetWaitHandleExtracted();
}
}
private ManualResetEvent TryGetWaitHandleExtracted()
{
var handle = Volatile.Read(ref _handle);
if (handle != null)
{
if (Thread.VolatileRead(ref _requested) == 2)
{
return handle;
}
handle.Close();
}
Thread.VolatileWrite(ref _requested, -1);
throw new ObjectDisposedException(GetType().FullName);
}
private bool WaitExtracted(int millisecondsTimeout)
{
var spinWait = new SpinWait();
var spinCount = _spinCount;
if (IsSet)
{
return true;
}
var start = ThreadingHelper.TicksNow();
retry_longTimeout:
if (IsSet)
{
return true;
}
var elapsed = ThreadingHelper.Milliseconds(ThreadingHelper.TicksNow() - start);
if (elapsed < millisecondsTimeout)
{
if (spinCount > 0)
{
spinCount--;
spinWait.SpinOnce();
goto retry_longTimeout;
}
var handle = RetriveWaitHandle();
var remaining = millisecondsTimeout - (int)elapsed;
if (remaining > 0)
{
return handle.WaitOne(remaining);
}
}
return false;
}
private bool WaitExtracted(int millisecondsTimeout, CancellationToken cancellationToken)
{
var spinWait = new SpinWait();
var spinCount = _spinCount;
if (IsSet)
{
return true;
}
var start = ThreadingHelper.TicksNow();
retry_longTimeout:
if (IsSet)
{
return true;
}
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
var elapsed = ThreadingHelper.Milliseconds(ThreadingHelper.TicksNow() - start);
if (elapsed < millisecondsTimeout)
{
if (spinCount > 0)
{
spinCount--;
spinWait.SpinOnce();
goto retry_longTimeout;
}
var handle = RetriveWaitHandle();
var remaining = millisecondsTimeout - (int)elapsed;
if (remaining > 0)
{
var result = WaitHandle.WaitAny
(
new[]
{
handle,
cancellationToken.WaitHandle
},
remaining
);
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
return result != WaitHandle.WaitTimeout;
}
}
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
return false;
}
private void WaitExtracted(CancellationToken cancellationToken)
{
var spinWait = new SpinWait();
var spinCount = _spinCount;
retry:
if (!IsSet)
{
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
if (spinCount > 0)
{
spinCount--;
spinWait.SpinOnce();
goto retry;
}
var handle = RetriveWaitHandle();
WaitHandle.WaitAny
(
new[]
{
handle,
cancellationToken.WaitHandle
}
);
cancellationToken.ThrowIfCancellationRequested();
GC.KeepAlive(cancellationToken.WaitHandle);
}
}
}
}
#endif