# 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()); } } ```