/// <summary>Performs the asynchronous wait.</summary> /// <param name="asyncWaiter">The asynchronous waiter.</param> /// <param name="millisecondsTimeout">The timeout.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task to return to the caller.</returns> private async Task <bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken) { Debug.Assert(asyncWaiter != null, "Waiter should have been constructed"); Debug.Assert(Monitor.IsEntered(m_lockObjAndDisposed), "Requires the lock be held"); await new ConfiguredNoThrowAwaiter <bool>(asyncWaiter.WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken)); if (cancellationToken.IsCancellationRequested) { // If we might be running as part of a cancellation callback, force the completion to be asynchronous // so as to maintain semantics similar to when no token is passed (neither Release nor Cancel would invoke // continuations off of this task). await TaskScheduler.Default; } if (asyncWaiter.IsCompleted) { return(true); // successfully acquired } // The wait has timed out or been canceled. // If the await completed synchronously, we still hold the lock. If it didn't, // we no longer hold the lock. As such, acquire it. lock (m_lockObjAndDisposed) { // Remove the task from the list. If we're successful in doing so, // we know that no one else has tried to complete this waiter yet, // so we can safely cancel or timeout. if (RemoveAsyncWaiter(asyncWaiter)) { cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred return(false); // timeout occurred } } // The waiter had already been removed, which means it's already completed or is about to // complete, so let it, and don't return until it does. return(await asyncWaiter.ConfigureAwait(false)); }