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
}
}