I'm implementing an AsyncManualResetEvent based on Stephen Toub's example. However, I would like to know if the event, or specifically, the underlying Task<T>
has been waited on.
I've already investigated the Task
class, and there doesn't seem to be a sensible way to determine if it has ever been 'awaited' or if a continuation has been added.
In this case however, I control access to the underlying task source, so I can listen for any calls to the WaitAsync
method instead. In thinking about how to do this, I decided to use a Lazy<T>
and just see if it has been 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;
}
My question then, is whether this is a safe approach, specifically will this guarantee that there are never any orphaned/lost continuations while the event is being reset?
If you truly wanted to know if anyone called
await
on your task (not just the fact that they calledWaitAsync()
) you could make a custom awaiter that acts as a wrapper for theTaskAwaiter
that is used bym_tcs.Task
.Now if anyone registered a continuation with
OnCompleted
orUnsafeOnCompleted
the boolHasWaiters
will becometrue
.I also added
TaskCreationOptions.RunContinuationsAsynchronously
to fix the issue Stephen fixes with theTask.Factory.StartNew
at the end of the article (It was introduced to .NET after the article was written).If you just want to see if anyone called WaitAsync you can simplify it a lot, you just need a class to hold your flag and your completion source.