コード例 #1
0
        /// <summary>
        /// Starts waiting on the <see cref="System.Threading.WaitHandle"/> to pulse on a separate thread
        /// </summary>
        /// <param name="waitHandle">The <see cref="System.Threading.WaitHandle"/> to wait on</param>
        /// <param name="timeoutMs">The wait timeout, or -1 to wait indefinitely</param>
        /// <param name="cancellationToken">The wait cancellation token</param>
        /// <returns>An instance of the awaiter</returns>
        internal static WaitHandleAwaiter StartWaiting(WaitHandle waitHandle, int timeoutMs, CancellationToken cancellationToken)
        {
            WaitHandleAsyncOperations.ThrowIfMutex(waitHandle);

            cancellationToken.ThrowIfCancellationRequested();

            var awaiter = new WaitHandleAwaiter()
            {
                _waitHandle = waitHandle, _timeoutMs = timeoutMs
            };

            awaiter._waitRegistration = ThreadPool.RegisterWaitForSingleObject(waitHandle, WaitCallback, awaiter, timeoutMs, executeOnlyOnce: true);

            if (cancellationToken.CanBeCanceled)
            {
                awaiter._ctRegistration = cancellationToken.Register(awaiter.OnCancelRequested, useSynchronizationContext: false);
            }

            return(awaiter);
        }
コード例 #2
0
 private static void CancelAwaiter(WaitHandleAwaiter awaiter)
 {
     awaiter.OnCompleted(null);
     awaiter.OnCancelRequested();
 }
コード例 #3
0
        /// <summary>
        /// Waits for any of given <see cref="System.Threading.WaitHandle"/> to pulse. Unlike <see cref="System.Threading.WaitHandle.WaitAny(WaitHandle[])"/>, this method does not wait on all of them as an atomic operation, but registers awaiters for given wait handles one by one, and there is no restriction of maximum 64 handles.
        /// </summary>
        /// <param name="waitHandles">The collection of <see cref="System.Threading.WaitHandle"/>s to wait on: <see cref="System.Threading.AutoResetEvent"/>, <see cref="System.Threading.ManualResetEvent"/>, or <see cref="System.Threading.Semaphore"/></param>
        /// <param name="timeoutMs">The wait timeout in milliseconds. Set to -1 to wait indefinitely. If all time out this method returns <see cref="System.Threading.WaitHandle.WaitTimeout"/>.</param>
        /// <param name="cancellationToken">The cancellation token to stop waiting. If cancellation is requested, the Task will throw <see cref="System.OperationCanceledException"/>.</param>
        /// <returns>Returns the index of a <see cref="System.Threading.WaitHandle"/> in the given collection which pulsed first, or <see cref="System.Threading.WaitHandle.WaitTimeout"/> if all of them timed out. Note that this method does not wait on all of them as an atomic operation, but registers awaiters for given wait handles one by one.</returns>
        public static Task <int> WaitAnyAsync(IEnumerable <WaitHandle> waitHandles, int timeoutMs, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (waitHandles == null)
            {
                throw new ArgumentNullException(nameof(waitHandles));
            }

            int totalCount = 0;

            foreach (var waitHandle in waitHandles)
            {
                if (waitHandle == null)
                {
                    throw new ArgumentException($"The WaitHandle reference at index {totalCount} is NULL", nameof(waitHandles));
                }
                ThrowIfMutex(waitHandle);
                totalCount++;
            }

            cancellationToken.ThrowIfCancellationRequested();

            var tcs         = new TaskCompletionSource <int>();
            var awaiters    = new WaitHandleAwaiter[totalCount];
            int failedCount = 0;
            int state       = 0;

            var ctRegistration = default(CancellationTokenRegistration);

            if (cancellationToken.CanBeCanceled)
            {
                ctRegistration = cancellationToken.Register(() => {
                    if (Interlocked.CompareExchange(ref state, 1, 0) == 0)
                    {
                        ctRegistration.Dispose();
                        tcs.SetCanceled();
                    }
                },
                                                            useSynchronizationContext: false);
            }

            int index = 0;

            foreach (var waitHandle in waitHandles)
            {
                if (state != 0)
                {
                    break;
                }

                var awaiter = awaiters[index] = waitHandle.ConfigureAwait(timeoutMs, cancellationToken).GetAwaiter();

                if (state != 0)
                {
                    CancelAwaiter(awaiter);
                    break;
                }

                var awaiterIndex = index;

                awaiter.OnCompleted(() => {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        if (TryChangeState(ref state, expectedState: 0, newState: 1))
                        {
                            ctRegistration.Dispose();
                            tcs.SetCanceled();
                        }
                    }
                    else if (awaiter.IsSignaled)
                    {
                        if (TryChangeState(ref state, expectedState: 0, newState: 1))
                        {
                            CancelAwaiters(awaiters);
                            ctRegistration.Dispose();
                            tcs.SetResult(awaiterIndex);
                        }
                    }
                    else
                    {
                        if (Interlocked.Increment(ref failedCount) == totalCount)
                        {
                            if (TryChangeState(ref state, expectedState: 0, newState: 1))
                            {
                                ctRegistration.Dispose();
                                tcs.SetResult(WaitHandle.WaitTimeout);
                            }
                        }
                    }
                });

                index++;
            }

            return(tcs.Task);
        }
コード例 #4
0
 /// <summary>
 /// Gets an awaiter used to await the <see cref="System.Threading.WaitHandle"/>
 /// </summary>
 /// <returns>An awaiter instance</returns>
 public WaitHandleAwaiter GetAwaiter() => WaitHandleAwaiter.StartWaiting(_waitHandle, _timeoutMs, _cancellationToken);