using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace ZeroLevel.Services.Async { /// /// An async-compatible manual-reset event. /// [DebuggerDisplay("Id = {Id}, IsSet = {GetStateForDebugger}")] [DebuggerTypeProxy(typeof(DebugView))] public sealed class AsyncManualResetEvent { /// /// The object used for synchronization. /// private readonly object _mutex; /// /// The current state of the event. /// private TaskCompletionSource _tcs; /// /// The semi-unique identifier for this instance. This is 0 if the id has not yet been created. /// private int _id; [DebuggerNonUserCode] private bool GetStateForDebugger { get { return _tcs.Task.IsCompleted; } } /// /// Creates an async-compatible manual-reset event. /// /// Whether the manual-reset event is initially set or unset. public AsyncManualResetEvent(bool set) { _mutex = new object(); _tcs = TaskCompletionSourceExtensions.CreateAsyncTaskSource(); if (set) _tcs.TrySetResult(null); } /// /// Creates an async-compatible manual-reset event that is initially unset. /// public AsyncManualResetEvent() : this(false) { } /// /// Gets a semi-unique identifier for this asynchronous manual-reset event. /// public int Id { get { return IdManager.GetId(ref _id); } } /// /// Whether this event is currently set. This member is seldom used; code using this member has a high possibility of race conditions. /// public bool IsSet { get { lock (_mutex) return _tcs.Task.IsCompleted; } } /// /// Asynchronously waits for this event to be set. /// public Task WaitAsync() { lock (_mutex) { return _tcs.Task; } } /// /// Asynchronously waits for this event to be set or for the wait to be canceled. /// /// The cancellation token used to cancel the wait. If this token is already canceled, this method will first check whether the event is set. public Task WaitAsync(CancellationToken cancellationToken) { var waitTask = WaitAsync(); if (waitTask.IsCompleted) return waitTask; return waitTask.WaitAsync(cancellationToken); } /// /// Synchronously waits for this event to be set. This method may block the calling thread. /// public void Wait() { WaitAsync().WaitAndUnwrapException(); } /// /// Synchronously waits for this event to be set. This method may block the calling thread. /// /// The cancellation token used to cancel the wait. If this token is already canceled, this method will first check whether the event is set. public void Wait(CancellationToken cancellationToken) { var ret = WaitAsync(); if (ret.IsCompleted) return; ret.WaitAndUnwrapException(cancellationToken); } /// /// Sets the event, atomically completing every task returned by . If the event is already set, this method does nothing. /// public void Set() { lock (_mutex) { _tcs.TrySetResult(null); } } /// /// Resets the event. If the event is already reset, this method does nothing. /// public void Reset() { lock (_mutex) { if (_tcs.Task.IsCompleted) _tcs = TaskCompletionSourceExtensions.CreateAsyncTaskSource(); } } // ReSharper disable UnusedMember.Local [DebuggerNonUserCode] private sealed class DebugView { private readonly AsyncManualResetEvent _mre; public DebugView(AsyncManualResetEvent mre) { _mre = mre; } public int Id { get { return _mre.Id; } } public bool IsSet { get { return _mre.GetStateForDebugger; } } public Task CurrentTask { get { return _mre._tcs.Task; } } } // ReSharper restore UnusedMember.Local } }