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 Task <bool> WaitToReadAsync(CancellationToken cancellationToken) { // Outside of the lock, check if there are any items waiting to be read. If there are, we're done. return(!_parent._items.IsEmpty ? ChannelUtilities.s_trueTask : WaitToReadAsyncCore(cancellationToken)); Task <bool> WaitToReadAsyncCore(CancellationToken ct) { // Now check for cancellation. if (ct.IsCancellationRequested) { return(Task.FromCanceled <bool>(ct)); } SingleConsumerUnboundedChannel <T> parent = _parent; ReaderInteractor <bool> oldWaiter = null, newWaiter; lock (parent.SyncObj) { // Again while holding the lock, check to see if there are any items available. if (!parent._items.IsEmpty) { return(ChannelUtilities.s_trueTask); } // There aren't any items; if we're done writing, there never will be more items. if (parent._doneWriting != null) { return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? Task.FromException <bool>(parent._doneWriting) : ChannelUtilities.s_falseTask); } // Create the new waiter. We're a bit more tolerant of a stray waiting reader // than we are of a blocked reader, as with usage patterns it's easier to leave one // behind, so we just cancel any that may have been waiting around. oldWaiter = parent._waitingReader; parent._waitingReader = newWaiter = ReaderInteractor <bool> .Create(parent._runContinuationsAsynchronously, ct); } oldWaiter?.TrySetCanceled(); return(newWaiter.Task); } }
internal UnboundedChannelReader(SingleConsumerUnboundedChannel <T> parent) => _parent = parent;