Example #1
0
            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));
                }
            }
Example #2
0
        /// <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;
            }
        }
Example #3
0
        /// <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;
            }
        }
Example #4
0
        /// <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);
                }
            }
Example #7
0
            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));
                }
            }