Deploy AsyncManualResetEvent using Lazy <T> to determine if the expected task was
I am implementing AsyncManualResetEvent based on the example of Stephen Tub . However, I would like to know if the event was expected or, in particular, the base one Task<T>.
I have already explored the class Task, and there seems to be no reasonable way to determine if it has ever been "expected" or a sequel has been added.
In this case, however, I control access to the base source of the job, so I can listen to any method calls WaitAsync. Thinking about how to do this, I decided to use it Lazy<T>and just see if it was created.
sealed class AsyncManualResetEvent {
public bool HasWaiters => tcs.IsValueCreated;
public AsyncManualResetEvent() {
Reset();
}
public Task WaitAsync() => tcs.Value.Task;
public void Set() {
if (tcs.IsValueCreated) {
tcs.Value.TrySetResult(result: true);
}
}
public void Reset() {
tcs = new Lazy<TaskCompletionSource<bool>>(LazyThreadSafetyMode.PublicationOnly);
}
Lazy<TaskCompletionSource<bool>> tcs;
}
, , , , / , reset?
, - await ( , WaitAsync()), awaiter, TaskAwaiter, m_tcs.Task.
public class AsyncManualResetEvent
{
private volatile Completion _completion = new Completion();
public bool HasWaiters => _completion.HasWaiters;
public Completion WaitAsync()
{
return _completion;
}
public void Set()
{
_completion.Set();
}
public void Reset()
{
while (true)
{
var completion = _completion;
if (!completion.IsCompleted ||
Interlocked.CompareExchange(ref _completion, new Completion(), completion) == completion)
return;
}
}
}
public class Completion
{
private readonly TaskCompletionSource<bool> _tcs;
private readonly CompletionAwaiter _awaiter;
public Completion()
{
_tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_awaiter = new CompletionAwaiter(_tcs.Task, this);
}
public CompletionAwaiter GetAwaiter() => _awaiter;
public bool IsCompleted => _tcs.Task.IsCompleted;
public bool HasWaiters { get; private set; }
public void Set() => _tcs.TrySetResult(true);
public struct CompletionAwaiter : ICriticalNotifyCompletion
{
private readonly TaskAwaiter _taskAwaiter;
private readonly Completion _parent;
internal CompletionAwaiter(Task task, Completion parent)
{
_parent = parent;
_taskAwaiter = task.GetAwaiter();
}
public bool IsCompleted => _taskAwaiter.IsCompleted;
public void GetResult() => _taskAwaiter.GetResult();
public void OnCompleted(Action continuation)
{
_parent.HasWaiters = true;
_taskAwaiter.OnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
_parent.HasWaiters = true;
_taskAwaiter.UnsafeOnCompleted(continuation);
}
}
}
, - OnCompleted UnsafeOnCompleted, bool HasWaiters true.
TaskCreationOptions.RunContinuationsAsynchronously, , Task.Factory.StartNew ( .NET ).
, - WaitAsync, , .
public class AsyncManualResetEvent
{
private volatile CompletionWrapper _completionWrapper = new CompletionWrapper();
public Task WaitAsync()
{
var wrapper = _completionWrapper;
wrapper.WaitAsyncCalled = true;
return wrapper.Tcs.Task;
}
public bool WaitAsyncCalled
{
get { return _completionWrapper.WaitAsyncCalled; }
}
public void Set() {
_completionWrapper.Tcs.TrySetResult(true); }
public void Reset()
{
while (true)
{
var wrapper = _completionWrapper;
if (!wrapper.Tcs.Task.IsCompleted ||
Interlocked.CompareExchange(ref _completionWrapper, new CompletionWrapper(), wrapper) == wrapper)
return;
}
}
private class CompletionWrapper
{
public TaskCompletionSource<bool> Tcs { get; } = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
public bool WaitAsyncCalled { get; set; }
}
}