/// <summary> /// Attempts to enqueue an item. /// </summary> /// <param name="item">The item to enqueue.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort the enqueue operation. If <paramref name="abort"/> is not <c>null</c>, then this token must include signals from the <paramref name="abort"/> object.</param> /// <param name="abort">A synchronization object used to cancel related enqueue operations. May be <c>null</c> if this is the only enqueue operation.</param> internal async Task <AsyncProducerConsumerQueue <T> > TryEnqueueAsync(T item, CancellationToken cancellationToken, TaskCompletionSource abort) { try { using (var combinedToken = CancellationTokenHelpers.Normalize(_completed.Token, cancellationToken)) using (await _mutex.LockAsync().ConfigureAwait(false)) { // Wait for the queue to be not full. while (Full) { await _notFull.WaitAsync(combinedToken.Token).ConfigureAwait(false); } // Explicitly check whether the queue has been marked complete to prevent a race condition where notFull is signalled at the same time the queue is marked complete. if (_completed.IsCancellationRequested) { return(null); } // Set the abort signal. If another queue has already set the abort signal, then abort. if (abort != null && !abort.TrySetCanceled()) { return(null); } _queue.Enqueue(item); _completedOrNotEmpty.Notify(); return(this); } } catch (OperationCanceledException) { return(null); } }
/// <summary> /// Attempts to enqueue an item. This method may block the calling thread. /// </summary> /// <param name="item">The item to enqueue.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort the enqueue operation.</param> internal AsyncProducerConsumerQueue <T> DoTryEnqueue(T item, CancellationToken cancellationToken) { try { using (var combinedToken = CancellationTokenHelpers.Normalize(_completed.Token, cancellationToken)) using (_mutex.Lock()) { // Wait for the queue to be not full. while (Full) { _notFull.Wait(combinedToken.Token); } // Explicitly check whether the queue has been marked complete to prevent a race condition where notFull is signalled at the same time the queue is marked complete. if (_completed.IsCancellationRequested) { return(null); } _queue.Enqueue(item); _completedOrNotEmpty.Notify(); return(this); } } catch (OperationCanceledException) { return(null); } }
/// <summary> /// Attempts to enqueue an item to any of a number of producer/consumer queues. Returns the producer/consumer queue that received the item. Returns <c>null</c> if all producer/consumer queues have completed adding. /// </summary> /// <param name="queues">The producer/consumer queues.</param> /// <param name="item">The item to enqueue.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort the enqueue operation.</param> /// <returns>The producer/consumer queue that received the item.</returns> public static async Task <AsyncProducerConsumerQueue <T> > TryEnqueueToAnyAsync <T>(this IEnumerable <AsyncProducerConsumerQueue <T> > queues, T item, CancellationToken cancellationToken) { var abort = new TaskCompletionSource(); using (var abortCancellationToken = CancellationTokenHelpers.FromTask(abort.Task)) using (var combinedToken = CancellationTokenHelpers.Normalize(abortCancellationToken.Token, cancellationToken)) { var token = combinedToken.Token; var tasks = queues.Select(q => q.TryEnqueueAsync(item, token, abort)); var results = await TaskShim.WhenAll(tasks).ConfigureAwait(false); var ret = results.FirstOrDefault(x => x != null); if (ret == null) { cancellationToken.ThrowIfCancellationRequested(); } return(ret); } }
/// <summary> /// Attempts to take an item from any of a number of producer/consumer collections. The operation "fails" if all the producer/consumer collections have completed adding and are empty, or if any take operation on an underlying collection fails. /// </summary> /// <param name="collections">The producer/consumer collections.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort the take operation.</param> public static async Task <AsyncCollection <T> .TakeResult> TryTakeFromAnyAsync <T>(this IEnumerable <AsyncCollection <T> > collections, CancellationToken cancellationToken) { var abort = new TaskCompletionSource(); using (var abortCancellationToken = CancellationTokenHelpers.FromTask(abort.Task)) using (var combinedToken = CancellationTokenHelpers.Normalize(abortCancellationToken.Token, cancellationToken)) { var token = combinedToken.Token; var tasks = collections.Select(q => q.TryTakeAsync(token, abort)); var results = await TaskShim.WhenAll(tasks).ConfigureAwait(false); var result = results.FirstOrDefault(x => x.Success); if (result != null) { return(result); } cancellationToken.ThrowIfCancellationRequested(); return(AsyncCollection <T> .FalseResult); } }