# Awaiting a WaitHandle
The following code is not my own but from the article [Async and cancellation support for wait handles](https://thomaslevesque.com/2015/06/04/async-and-cancellation-support-for-wait-handles/).
Important: `RegisterWaitForSingleObject` works on `WaitHandles` other than `Mutex`. To quote the .NET documentation:
> Using a [Mutex](https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8) for `waitObject` does not provide mutual exclusion for the callbacks because the underlying Windows API uses the default `WT_EXECUTEDEFAULT` flag, so each callback is dispatched on a separate thread pool thread. Instead of a [Mutex](https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8), use a [Semaphore](https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphore?view=netframework-4.8) with a maximum count of 1.
```csharp
/// Returns true if the wait handle is signaled. False if timeout occurred.
public static async Task<bool> WaitOneAsync(
    this WaitHandle handle,
    int millisecondsTimeout = -1,
    CancellationToken? cancellationToken = null)
{
    RegisteredWaitHandle? registeredHandle = null;
    var tokenRegistration = default(CancellationTokenRegistration);
    try
    {
        var tcs = new TaskCompletionSource<bool>();
        registeredHandle = ThreadPool.RegisterWaitForSingleObject(
            handle,
            (state, timedOut) => ((TaskCompletionSource<bool>)state!).TrySetResult(!timedOut),
            tcs,
            millisecondsTimeout,
            true);
        if (cancellationToken is { } ct)
        {
            tokenRegistration = ct.Register(
                state => ((TaskCompletionSource<bool>)state!).TrySetCanceled(),
                tcs);
        }
        return await tcs.Task;
    }
    finally
    {
        registeredHandle?.Unregister(null);
        var _ = Task.Run(() => tokenRegistration.Dispose());
    }
}
```