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