using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace CryptoExchange.Net.Objects { /// /// Async auto reset based on Stephen Toub`s implementation /// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-2-asyncautoresetevent/ /// public class AsyncResetEvent : IDisposable { private static readonly Task _completed = Task.FromResult(true); private Queue> _waits = new Queue>(); private bool _signaled; private readonly bool _reset; /// /// New AsyncResetEvent /// /// /// public AsyncResetEvent(bool initialState = false, bool reset = true) { _signaled = initialState; _reset = reset; } /// /// Wait for the AutoResetEvent to be set /// /// public Task WaitAsync(TimeSpan? timeout = null, CancellationToken ct = default) { lock (_waits) { if (_signaled) { if(_reset) _signaled = false; return _completed; } else { if (ct.IsCancellationRequested) return _completed; var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); if (timeout.HasValue) { var timeoutSource = new CancellationTokenSource(timeout.Value); var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, ct); ct = cancellationSource.Token; } var registration = ct.Register(() => { lock (_waits) { tcs.TrySetResult(false); // Not the cleanest but it works _waits = new Queue>(_waits.Where(i => i != tcs)); } }, useSynchronizationContext: false); _waits.Enqueue(tcs); return tcs.Task; } } } /// /// Signal a waiter /// public void Set() { lock (_waits) { if (!_reset) { // Act as ManualResetEvent. Once set keep it signaled and signal everyone who is waiting _signaled = true; while (_waits.Count > 0) { var toRelease = _waits.Dequeue(); toRelease.TrySetResult(true); } } else { // Act as AutoResetEvent. When set signal 1 waiter if (_waits.Count > 0) { var toRelease = _waits.Dequeue(); toRelease.TrySetResult(true); } else if (!_signaled) _signaled = true; } } } /// /// Dispose /// public void Dispose() { _waits.Clear(); } } }