Reputation: 1329
C# has a recommended method to wait for single WaitHandle object in non-blocking way through ThreadPool.RegisterWaitForSingleObject
. But I need something similar but for multiple objects. Something like WaitHandle.WaitAll
but in async variant. How to achieve this in less resources wasteful way? Now I think about task creation and wait the handles there, something like this:
public static class WaitHandleExtension
{
public static Task<bool> WaitAllAsync ( this WaitHandle[] handles )
{
return Task.Factory.StartNew( () => WaitHandle.WaitAll( handles ) );
}
}
But I can't implement cancellation in such approach. What will be the best way to wait all handles async with cancellation support?
UPD: Here I describe a task I'm solving, maybe someone will suggest a different way to implement it in async:
I'm implementing cross-platform IPC stream that works like named pipe but in simplified manner. It should be fully async and support cancellation. It should support Mono on macOS and Windows, because server app relies on Mono and can't use .NET on Windows. The problem is: Mono on Windows doesn't support UNIX domain sockets at all and named pipes implementation is not complete (named pipe connection can't be canceled on Mono Windows). I implemented macOS part on UNIX domain sockets and it works great. On Windows I decided to implement my own Stream class based on Memory Mapped File. It has two shared read-write buffers for duplex data exchange. Each buffer protected by named Semaphore and has two named events EventWaitHandle, one signals that buffer is not empty and another that buffer is not full. So, Send method waits for semaphore and non-full event to fill the buffer with data and signal non-empty event, if the buffer is fulfilled it resets non-full event. Read waits for semaphore and non-empty event to consume data and signal non-full event, if it consumes all the data it resets non-empty event. It works perfectly in sync way. But it would be great to implement cancellable ReadAsync and WriteAsync.
Upvotes: 1
Views: 994
Reputation: 6060
This works, but feels a bit dirty. The idea is to efficiently wait for any one handle to complete, including the cancel token's handle. If one of the handles (other than the cancel token) completes, remove it from the list of remaining handles and efficiently wait again, until all the handles have completed OR the cancel token wait handle has completed.
static async Task<bool> WaitForHandlesAsync(WaitHandle[] handles, CancellationToken cancellationToken) {
List<WaitHandle> remainingHandles = new List<WaitHandle>(handles.Length + 1);
remainingHandles.Add(cancellationToken.WaitHandle);
remainingHandles.AddRange(handles);
bool canceled = cancellationToken.IsCancellationRequested;
while (remainingHandles.Count > 1 && !canceled) {
int idx = await Task.Factory.StartNew(() => WaitHandle.WaitAny(remainingHandles.ToArray()));
if (idx == 0)
canceled = true;
else {
remainingHandles.RemoveAt(idx);
}
}
return !canceled;
}
Upvotes: 0