Waiting for a ManualResetEventSlim to be set asynchronously

 
 
  • Gérald Barré

ManualResetEventSlim represents a thread synchronization event that, when signaled, must be reset manually. This is a common synchronization primitive. However, it doesn't expose a method to wait asynchronously. Hopefully, it's not too complicated to create an extension method using ThreadPool.RegisterWaitForSingleObject and TaskCompletionSource.

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 the previous 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?Buy Me A Coffee💖 Sponsor on GitHub