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))); } if (TryRead(out T item)) { return(new ValueTask <T>(item)); } SingleConsumerUnboundedChannel <T> parent = _parent; AsyncOperation <T> oldBlockedReader, newBlockedReader; lock (parent.SyncObj) { // Now that we hold the lock, try reading again. if (TryRead(out item)) { return(new ValueTask <T>(item)); } // If no more items will be written, fail the read. if (parent._doneWriting != null) { return(ChannelUtilities.GetInvalidCompletionValueTask <T>(parent._doneWriting)); } // Try to use the singleton reader. If it's currently being used, then the channel // is being used erroneously, and we cancel the outstanding operation. oldBlockedReader = parent._blockedReader; if (!cancellationToken.CanBeCanceled && _readerSingleton.TryOwnAndReset()) { newBlockedReader = _readerSingleton; if (newBlockedReader == oldBlockedReader) { // The previous operation completed, so null out the "old" reader // so we don't end up canceling the new operation. oldBlockedReader = null; } } else { newBlockedReader = new AsyncOperation <T>(_parent._runContinuationsAsynchronously, cancellationToken); } parent._blockedReader = newBlockedReader; } oldBlockedReader?.TrySetCanceled(); return(newBlockedReader.ValueTaskOfT); }
public override ValueTask <T> ReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken))); } // Dequeue an item if we can. UnboundedChannel <T> parent = _parent; if (parent._items.TryDequeue(out T item)) { CompleteIfDone(parent); return(new ValueTask <T>(item)); } lock (parent.SyncObj) { parent.AssertInvariants(); // Try to dequeue again, now that we hold the lock. if (parent._items.TryDequeue(out item)) { CompleteIfDone(parent); return(new ValueTask <T>(item)); } // There are no items, so if we're done writing, 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, create and queue a reader. var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously, cancellationToken); parent._blockedReaders.EnqueueTail(reader); return(reader.ValueTaskOfT); } }
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); } }
private ValueTask <T> ReadAsyncCore(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken))); } UnboundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // If there are any items, return one. if (parent._items.TryDequeue(out T item)) { // Dequeue an 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(new ValueTask <T>(item)); } // There are no items, so if we're done writing, 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, create and queue a reader. var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously, cancellationToken); parent._blockedReaders.EnqueueTail(reader); return(reader.ValueTaskOfT); } }
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 ValueTask <T> ReadAsync(CancellationToken cancellationToken) { { return(TryRead(out T item) ? new ValueTask <T>(item) : ReadAsyncCore(cancellationToken)); } ValueTask <T> ReadAsyncCore(CancellationToken ct) { SingleConsumerUnboundedChannel <T> parent = _parent; if (ct.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(ct))); } lock (parent.SyncObj) { // Now that we hold the lock, try reading again. if (TryRead(out T item)) { return(new ValueTask <T>(item)); } // If no more items will be written, fail the read. if (parent._doneWriting != null) { return(ChannelUtilities.GetInvalidCompletionValueTask <T>(parent._doneWriting)); } Debug.Assert(parent._blockedReader == null || parent._blockedReader.Task.IsCanceled, "Incorrect usage; multiple outstanding reads were issued against this single-consumer channel"); // Store the reader to be completed by a writer. var reader = ReaderInteractor <T> .Create(parent._runContinuationsAsynchronously, ct); parent._blockedReader = reader; return(new ValueTask <T>(reader.Task)); } } }
private ValueTask <T> ReadAsyncCore(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken))); } UnboundedChannel <T> parent = _parent; lock (parent.SyncObj) { parent.AssertInvariants(); // If there are any items, return one. if (parent._items.TryDequeue(out T item)) { // Dequeue an 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(new ValueTask <T>(item)); } // There are no items, so if we're done writing, 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)); } }