/// <summary>Asynchronously writes an item to the channel.</summary> /// <param name="item">The value to write to the channel.</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the write operation.</param> /// <returns>A <see cref="Task"/> that represents the asynchronous write operation.</returns> public virtual Task WriteAsync(T item, CancellationToken cancellationToken = default(CancellationToken)) { try { return (cancellationToken.IsCancellationRequested ? Task.FromCanceled <T>(cancellationToken) : TryWrite(item) ? Task.CompletedTask : WriteAsyncCore(item, cancellationToken)); } catch (Exception e) { return(Task.FromException(e)); } async Task WriteAsyncCore(T innerItem, CancellationToken ct) { while (await WaitToWriteAsync(ct).ConfigureAwait(false)) { if (TryWrite(innerItem)) { return; } } throw ChannelUtilities.CreateInvalidCompletionException(); } }
/// <summary>Mark the channel as being complete, meaning no more items will be written to it.</summary> /// <param name="error">Optional Exception indicating a failure that's causing the channel to complete.</param> /// <exception cref="InvalidOperationException">The channel has already been marked as complete.</exception> public void Complete(Exception error = null) { if (!TryComplete(error)) { throw ChannelUtilities.CreateInvalidCompletionException(); } }
private async Task WriteAsyncCore(T innerItem, CancellationToken ct) { while (await WaitToWriteAsync(ct).ConfigureAwait(false)) { if (TryWrite(innerItem)) { return; } } throw ChannelUtilities.CreateInvalidCompletionException(); }
public override ValueTask WriteAsync(T item, CancellationToken cancellationToken) => cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : TryWrite(item) ? default : new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)));
public override bool TryComplete(Exception error) { UnboundedChannel <T> parent = _parent; bool completeTask; lock (parent.SyncObj) { parent.AssertInvariants(); // If we've already marked the channel as completed, bail. if (parent._doneWriting != null) { return(false); } // Mark that we're done writing. parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel; completeTask = parent._items.IsEmpty; } // If there are no items in the queue, complete the channel's task, // as no more data can possibly arrive at this point. We do this outside // of the lock in case we'll be running synchronous completions, and we // do it before completing blocked/waiting readers, so that when they // wake up they'll see the task as being completed. if (completeTask) { ChannelUtilities.Complete(parent._completion, error); } // At this point, _blockedReaders and _waitingReaders will not be mutated: // they're only mutated by readers while holding the lock, and only if _doneWriting is null. // freely manipulate _blockedReaders and _waitingReaders without any concurrency concerns. ChannelUtilities.FailOperations <AsyncOperation <T>, T>(parent._blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error)); ChannelUtilities.WakeUpWaiters(ref parent._waitingReadersTail, result: false, error: error); // Successfully transitioned to completed. return(true); }
public override ValueTask WriteAsync(T item, CancellationToken cancellationToken) => // Writing always succeeds (unless we've already completed writing or cancellation has been requested), // so just TryWrite and return a completed task. cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : TryWrite(item) ? default : new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)));
public override bool TryComplete(Exception error) { AsyncOperation <T> blockedReader = null; AsyncOperation <bool> waitingReader = null; bool completeTask = false; SingleConsumerUnboundedChannel <T> parent = _parent; lock (parent.SyncObj) { // If we're already marked as complete, there's nothing more to do. if (parent._doneWriting != null) { return(false); } // Mark as complete for writing. parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel; // If we have no more items remaining, then the channel needs to be marked as completed // and readers need to be informed they'll never get another item. All of that needs // to happen outside of the lock to avoid invoking continuations under the lock. if (parent._items.IsEmpty) { completeTask = true; if (parent._blockedReader != null) { blockedReader = parent._blockedReader; parent._blockedReader = null; } if (parent._waitingReader != null) { waitingReader = parent._waitingReader; parent._waitingReader = null; } } } // Complete the channel task if necessary if (completeTask) { ChannelUtilities.Complete(parent._completion, error); } Debug.Assert(blockedReader == null || waitingReader == null, "There should only ever be at most one reader."); // Complete a blocked reader if necessary if (blockedReader != null) { error = ChannelUtilities.CreateInvalidCompletionException(error); blockedReader.TrySetException(error); } // Complete a waiting reader if necessary. (We really shouldn't have both a blockedReader // and a waitingReader, but it's more expensive to prevent it than to just tolerate it.) if (waitingReader != null) { if (error != null) { waitingReader.TrySetException(error); } else { waitingReader.TrySetResult(item: false); } } // Successfully completed the channel return(true); }