Wrapping ManualResetEvent as awaitable task

RegisterWaitForSingleObject will combine waits onto dedicated waiter threads, each of which can wait on multiple handles (specifically, 63 of them, which is MAXIMUM_WAIT_OBJECTS minus one for a “control” handle).

So you should be able to use something like this (warning: untested):

public static class WaitHandleExtensions
    public static Task AsTask(this WaitHandle handle)
        return AsTask(handle, Timeout.InfiniteTimeSpan);

    public static Task AsTask(this WaitHandle handle, TimeSpan timeout)
        var tcs = new TaskCompletionSource<object>();
        var registration = ThreadPool.RegisterWaitForSingleObject(handle, (state, timedOut) =>
            var localTcs = (TaskCompletionSource<object>)state;
            if (timedOut)
        }, tcs, timeout, executeOnlyOnce: true);
        tcs.Task.ContinueWith((_, state) => ((RegisteredWaitHandle)state).Unregister(null), registration, TaskScheduler.Default);
        return tcs.Task;

Leave a Comment