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 ValueTask <T> ReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken))); } BoundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // If there are any items, hand one back. if (!parent._items.IsEmpty) { return(new ValueTask <T>(DequeueItemAndPostProcess())); } // There weren't any items. If we're done writing so that there // will never be more items, fail. if (parent._doneWriting != null) { return(ChannelUtilities.GetInvalidCompletionValueTask <T>(parent._doneWriting)); } // Otherwise, queue the reader. var reader = ReaderInteractor <T> .Create(parent._runContinuationsAsynchronously, cancellationToken); parent._blockedReaders.EnqueueTail(reader); return(new ValueTask <T>(reader.Task)); } }
public override ValueTask <T> ReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken))); } BoundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // If there are any items, hand one back. if (!parent._items.IsEmpty) { return(new ValueTask <T>(DequeueItemAndPostProcess())); } // There weren't any items. If we're done writing so that there // will never be more items, fail. if (parent._doneWriting != null) { return(ChannelUtilities.GetInvalidCompletionValueTask <T>(parent._doneWriting)); } // If we're able to use the singleton reader, do so. if (!cancellationToken.CanBeCanceled) { AsyncOperation <T> singleton = _readerSingleton; if (singleton.TryOwnAndReset()) { parent._blockedReaders.EnqueueTail(singleton); return(singleton.ValueTaskOfT); } } // Otherwise, queue a reader. Note that in addition to checking whether synchronous continuations were requested, // we also check whether the supplied cancellation token can be canceled. The writer calls UnregisterCancellation // while holding the lock, and if a callback needs to be unregistered and is currently running, it needs to wait // for that callback to complete so that the subsequent code knows it won't be contending with another thread // trying to complete the operation. However, if we allowed a synchronous continuation from this operation, that // cancellation callback could end up running arbitrary code, including code that called back into the reader or // writer and tried to take the same lock held by the thread running UnregisterCancellation... deadlock. As such, // we only allow synchronous continuations here if both a) the caller requested it and the token isn't cancelable. var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously | cancellationToken.CanBeCanceled, cancellationToken); parent._blockedReaders.EnqueueTail(reader); return(reader.ValueTaskOfT); } }
public override ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <bool>(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(new ValueTask <bool>(true)); } // 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 ? new ValueTask <bool>(Task.FromException <bool>(parent._doneWriting)) : new ValueTask <bool>(false)); } // There were no items available, but there could be in the future, so ensure // there's a blocked reader task and return it. // If we're able to use the singleton waiter, do so. if (!cancellationToken.CanBeCanceled) { AsyncOperation <bool> singleton = _waiterSingleton; if (singleton.TryOwnAndReset()) { ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, singleton); return(singleton.ValueTaskOfT); } } // Otherwise, queue a reader. var waiter = new AsyncOperation <bool>(parent._runContinuationsAsynchronously, cancellationToken); ChannelUtilities.QueueWaiter(ref _parent._waitingReadersTail, waiter); return(waiter.ValueTaskOfT); } }
public override bool TryRead([MaybeNullWhen(false)] out T item) { BoundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // Get an item if there is one. if (!parent._items.IsEmpty) { item = DequeueItemAndPostProcess(); return(true); } } item = default !;
public override ValueTask <T> ReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken))); } BoundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // If there are any items, hand one back. if (!parent._items.IsEmpty) { return(new ValueTask <T>(DequeueItemAndPostProcess())); } // There weren't any items. If we're done writing so that there // will never be more items, fail. if (parent._doneWriting != null) { return(ChannelUtilities.GetInvalidCompletionValueTask <T>(parent._doneWriting)); } // If we're able to use the singleton reader, do so. if (!cancellationToken.CanBeCanceled) { AsyncOperation <T> singleton = _readerSingleton; if (singleton.TryOwnAndReset()) { parent._blockedReaders.EnqueueTail(singleton); return(singleton.ValueTaskOfT); } } // Otherwise, queue the reader. var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously, cancellationToken); parent._blockedReaders.EnqueueTail(reader); return(reader.ValueTaskOfT); } }
public override bool TryPeek([MaybeNullWhen(false)] out T item) { BoundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // Peek at an item if there is one. if (!parent._items.IsEmpty) { item = parent._items.PeekHead(); return(true); } } item = default; return(false); }
public override ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <bool>(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(new ValueTask <bool>(true)); } // 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 ? new ValueTask <bool>(Task.FromException <bool>(parent._doneWriting)) :