Waiting for a ManualResetEventSlim to be set asynchronously

 
 
  • Gérald Barré

ManualResetEventSlim represents a thread synchronization event that must be reset manually after being signaled. It is a common synchronization primitive, but it does not expose an asynchronous wait method. Fortunately, creating an extension method using ThreadPool.RegisterWaitForSingleObject and TaskCompletionSource is straightforward.

C#
static class Extensions
{
    public static Task WaitAsync(this ManualResetEventSlim manualResetEvent, CancellationToken cancellationToken = default)
        => WaitAsync(manualResetEvent.WaitHandle, cancellationToken);

    public static Task WaitAsync(this WaitHandle waitHandle, CancellationToken cancellationToken = default)
    {
        CancellationTokenRegistration cancellationRegistration = default;

        var tcs = new TaskCompletionSource();
        var handle = ThreadPool.RegisterWaitForSingleObject(
            waitObject: waitHandle,
            callBack: (o, timeout) =>
            {
                cancellationRegistration.Unregister();
                tcs.TrySetResult();
            },
            state: null,
            timeout: Timeout.InfiniteTimeSpan,
            executeOnlyOnce: true);

        if (cancellationToken.CanBeCanceled)
        {
            cancellationRegistration = cancellationToken.Register(() =>
            {
                handle.Unregister(waitHandle);
                tcs.TrySetCanceled(cancellationToken);
            });
        }

        return tcs.Task;
    }
}

You can test these extension methods using the following code:

C#
var manualResetEvent = new ManualResetEventSlim();
_ = Task.Run(async () =>
{
    await Task.Delay(2_000);
    Console.WriteLine("Unlock");
    manualResetEvent.Set();
});

await manualResetEvent.WaitAsync(cts.Token);
Console.WriteLine("Done");

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?