/// <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(); } }
/// <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(); } }
public override Task <bool> WaitToReadAsync(CancellationToken cancellationToken) { // If there are any items, readers can try to get them. return(!_parent._items.IsEmpty ? ChannelUtilities.s_trueTask : WaitToReadAsyncCore(cancellationToken)); Task <bool> WaitToReadAsyncCore(CancellationToken ct) { UnboundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // Try again to read now that we're synchronized with writers. if (!parent._items.IsEmpty) { return(ChannelUtilities.s_trueTask); } // There are no items, so if we're done writing, there's never going to be data available. if (parent._doneWriting != null) { return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? Task.FromException <bool>(parent._doneWriting) : ChannelUtilities.s_falseTask); } // Queue the waiter return(ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, parent._runContinuationsAsynchronously, ct)); } } }
public override Task <bool> WaitToReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <bool>(cancellationToken)); } BoundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // If there are any items available, a read is possible. if (!parent._items.IsEmpty) { return(ChannelUtilities.s_trueTask); } // There were no items available, so if we're done writing, a read will never be possible. if (parent._doneWriting != null) { return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? Task.FromException <bool>(parent._doneWriting) : ChannelUtilities.s_falseTask); } // There were no items available, but there could be in the future, so ensure // there's a blocked reader task and return it. return(ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, parent._runContinuationsAsynchronously, cancellationToken)); } }
public override bool TryRead(out T item) { SingleConsumerUnboundedChannel <T> parent = _parent; if (parent._items.TryDequeue(out item)) { if (parent._doneWriting != null && parent._items.IsEmpty) { ChannelUtilities.Complete(parent._completion, parent._doneWriting); } return(true); } return(false); }
public override bool TryRead(out T item) { UnboundedChannel <T> parent = _parent; // Dequeue an item if we can if (parent._items.TryDequeue(out item)) { if (parent._doneWriting != null && parent._items.IsEmpty) { // If we've now emptied the items queue and we're not getting any more, complete. ChannelUtilities.Complete(parent._completion, parent._doneWriting); } return(true); } item = default(T); return(false); }
/// <summary>Dequeues an item, and then fixes up our state around writers and completion.</summary> /// <returns>The dequeued item.</returns> private T DequeueItemAndPostProcess() { BoundedChannel <T> parent = _parent; Debug.Assert(Monitor.IsEntered(parent.SyncObj)); // Dequeue an item. T item = parent._items.DequeueHead(); // If we're now empty and we're done writing, complete the channel. if (parent._doneWriting != null && parent._items.IsEmpty) { ChannelUtilities.Complete(parent._completion, parent._doneWriting); } // If there are any writers blocked, there's now room for at least one // to be promoted to have its item moved into the items queue. We need // to loop while trying to complete the writer in order to find one that // hasn't yet been canceled (canceled writers transition to canceled but // remain in the physical queue). while (!parent._blockedWriters.IsEmpty) { WriterInteractor <T> w = parent._blockedWriters.DequeueHead(); if (w.Success(default(VoidResult))) { parent._items.EnqueueTail(w.Item); return(item); } } // There was no blocked writer, so see if there's a WaitToWriteAsync // we should wake up. ChannelUtilities.WakeUpWaiters(ref parent._waitingWriters, result: true); // Return the item return(item); }
public override Task <bool> WaitToReadAsync(CancellationToken cancellationToken) { UnbufferedChannel <T> parent = _parent; lock (parent.SyncObj) { // If we're done writing, fail. if (parent._completion.Task.IsCompleted) { return(parent._completion.Task.IsFaulted ? Task.FromException <bool>(parent._completion.Task.Exception.InnerException) : ChannelUtilities.s_falseTask); } // If there's a blocked writer, we can read. if (!parent._blockedWriters.IsEmpty) { return(ChannelUtilities.s_trueTask); } // Otherwise, queue the waiter. return(ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, true, cancellationToken)); } }