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.
264 lines
6.8 KiB
264 lines
6.8 KiB
#if NET20 || NET30 || NET35 || !NET_4_6 |
|
|
|
using System.Diagnostics; |
|
using LinqInternal.Threading; |
|
|
|
namespace System.Threading |
|
{ |
|
[DebuggerDisplay("Initial Count={InitialCount}, Current Count={CurrentCount}")] |
|
public class CountdownEvent : IDisposable |
|
{ |
|
private readonly ManualResetEventSlim _event; |
|
private int _currentCount; |
|
private volatile bool _disposed; |
|
private int _initialCount; |
|
|
|
public CountdownEvent(int initialCount) |
|
{ |
|
if (initialCount < 0) |
|
{ |
|
throw new ArgumentOutOfRangeException("initialCount"); |
|
} |
|
else |
|
{ |
|
_initialCount = initialCount; |
|
_currentCount = initialCount; |
|
_event = new ManualResetEventSlim(); |
|
if (initialCount == 0) |
|
{ |
|
_event.Set(); |
|
} |
|
} |
|
} |
|
|
|
public int CurrentCount |
|
{ |
|
get |
|
{ |
|
var currentCount = Thread.VolatileRead(ref _currentCount); |
|
return currentCount >= 0 ? currentCount : 0; |
|
} |
|
} |
|
|
|
public int InitialCount |
|
{ |
|
get { return _initialCount; } |
|
} |
|
|
|
public bool IsSet |
|
{ |
|
get { return Thread.VolatileRead(ref _currentCount) <= 0; } |
|
} |
|
|
|
public WaitHandle WaitHandle |
|
{ |
|
get |
|
{ |
|
CheckDisposed(); |
|
return _event.WaitHandle; |
|
} |
|
} |
|
|
|
public void AddCount() |
|
{ |
|
AddCount(1); |
|
} |
|
|
|
public void AddCount(int signalCount) |
|
{ |
|
if (!TryAddCount(signalCount)) |
|
{ |
|
throw new InvalidOperationException("Already Zero"); |
|
} |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
Dispose(true); |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
public void Reset() |
|
{ |
|
Reset(_initialCount); |
|
} |
|
|
|
public void Reset(int count) |
|
{ |
|
CheckDisposed(); |
|
if (count < 0) |
|
{ |
|
throw new ArgumentOutOfRangeException("count"); |
|
} |
|
else |
|
{ |
|
Thread.VolatileWrite(ref _currentCount, count); |
|
_initialCount = count; |
|
if (count == 0) |
|
{ |
|
_event.Set(); |
|
} |
|
else |
|
{ |
|
_event.Reset(); |
|
} |
|
} |
|
} |
|
|
|
public bool Signal() |
|
{ |
|
CheckDisposed(); |
|
if (Thread.VolatileRead(ref _currentCount) <= 0) |
|
{ |
|
throw new InvalidOperationException("Below Zero"); |
|
} |
|
else |
|
{ |
|
var currentCount = Interlocked.Decrement(ref _currentCount); |
|
if (currentCount == 0) |
|
{ |
|
_event.Set(); |
|
return true; |
|
} |
|
else |
|
{ |
|
if (currentCount < 0) |
|
{ |
|
throw new InvalidOperationException("Below Zero"); |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
public bool Signal(int signalCount) |
|
{ |
|
if (signalCount <= 0) |
|
{ |
|
throw new ArgumentOutOfRangeException("signalCount"); |
|
} |
|
else |
|
{ |
|
CheckDisposed(); |
|
int lastValue; |
|
if (ThreadingHelper.SpinWaitRelativeExchangeUnlessNegative(ref _currentCount, -signalCount, out lastValue)) |
|
{ |
|
var result = lastValue - signalCount; |
|
if (result == 0) |
|
{ |
|
_event.Set(); |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
throw new InvalidOperationException("Below Zero"); |
|
} |
|
} |
|
} |
|
|
|
public bool TryAddCount() |
|
{ |
|
return TryAddCount(1); |
|
} |
|
|
|
public bool TryAddCount(int signalCount) |
|
{ |
|
if (signalCount <= 0) |
|
{ |
|
throw new ArgumentOutOfRangeException("signalCount"); |
|
} |
|
else |
|
{ |
|
CheckDisposed(); |
|
int lastValue; |
|
if (ThreadingHelper.SpinWaitRelativeExchangeBounded(ref _currentCount, signalCount, 1, int.MaxValue, out lastValue)) |
|
{ |
|
return true; |
|
} |
|
else |
|
{ |
|
if (lastValue < 1) |
|
{ |
|
return false; |
|
} |
|
else |
|
{ |
|
throw new InvalidOperationException("Max"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public void Wait() |
|
{ |
|
Wait(-1, CancellationToken.None); |
|
} |
|
|
|
public void Wait(CancellationToken cancellationToken) |
|
{ |
|
Wait(-1, cancellationToken); |
|
} |
|
|
|
public bool Wait(TimeSpan timeout) |
|
{ |
|
var totalMilliseconds = (long)timeout.TotalMilliseconds; |
|
if (totalMilliseconds < -1L || totalMilliseconds > int.MaxValue) |
|
{ |
|
throw new ArgumentOutOfRangeException("timeout"); |
|
} |
|
else |
|
{ |
|
return Wait((int)totalMilliseconds, CancellationToken.None); |
|
} |
|
} |
|
|
|
public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) |
|
{ |
|
CheckDisposed(); |
|
cancellationToken.ThrowIfCancellationRequested(); |
|
var isSet = IsSet; |
|
if (!isSet) |
|
{ |
|
isSet = _event.Wait(timeout, cancellationToken); |
|
} |
|
return isSet; |
|
} |
|
|
|
public bool Wait(int millisecondsTimeout) |
|
{ |
|
return Wait(millisecondsTimeout, CancellationToken.None); |
|
} |
|
|
|
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) |
|
{ |
|
return Wait(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken); |
|
} |
|
|
|
protected virtual void Dispose(bool disposing) |
|
{ |
|
if (disposing) |
|
{ |
|
_event.Dispose(); |
|
_disposed = true; |
|
} |
|
} |
|
|
|
private void CheckDisposed() |
|
{ |
|
if (_disposed) |
|
{ |
|
throw new ObjectDisposedException("CountdownEvent"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
#endif |