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)); } }
/// <summary>Wake up all of the waiters and null out the field.</summary> /// <param name="waiters">The waiters.</param> /// <param name="result">The value with which to complete each waiter.</param> internal static void WakeUpWaiters(ref ReaderInteractor <bool> waiters, bool result) { ReaderInteractor <bool> w = waiters; if (w != null) { w.Success(result); waiters = null; } }
/// <summary>Wake up all of the waiters and null out the field.</summary> /// <param name="waiters">The waiters.</param> /// <param name="result">The success value with which to complete each waiter if <paramref name="error">error</paramref> is null.</param> /// <param name="error">The failure with which to cmplete each waiter, if non-null.</param> internal static void WakeUpWaiters(ref ReaderInteractor <bool> waiters, bool result, Exception error = null) { ReaderInteractor <bool> w = waiters; if (w != null) { if (error != null) { w.Fail(error); } else { w.Success(result); } waiters = null; } }
/// <summary>Gets or creates a "waiter" (e.g. WaitForRead/WriteAsync) interactor.</summary> /// <param name="waiter">The field storing the waiter interactor.</param> /// <param name="runContinuationsAsynchronously">true to force continuations to run asynchronously; otherwise, false.</param> /// <param name="cancellationToken">The token to use to cancel the wait.</param> internal static Task <bool> GetOrCreateWaiter(ref ReaderInteractor <bool> waiter, bool runContinuationsAsynchronously, CancellationToken cancellationToken) { // Get the existing waiters interactor. ReaderInteractor <bool> w = waiter; // If there isn't one, create one. This explicitly does not include the cancellation token, // as we reuse it for any number of waiters that overlap. if (w == null) { waiter = w = ReaderInteractor <bool> .Create(runContinuationsAsynchronously); } // If the cancellation token can't be canceled, then just return the waiter task. // If it can, we need to return a task that will complete when the waiter task does but that can also be canceled. // Easiest way to do that is with a cancelable continuation. return(cancellationToken.CanBeCanceled ? w.Task.ContinueWith(t => t.Result, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) : w.Task); }
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)); } } }
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 (cancellationToken.IsCancellationRequested ? Task.FromCanceled <bool>(cancellationToken) : !_parent._items.IsEmpty ? ChannelUtilities.s_trueTask : WaitToReadAsyncCore(cancellationToken)); Task <bool> WaitToReadAsyncCore(CancellationToken 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); } }
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)); } }