Exemple #1
0
        private ValueTask <T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            // Fast-path cancellation check
            if (cancellationToken.IsCancellationRequested)
            {
                return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
            }

            lock (SyncObj)
            {
                AssertInvariants();

                // If there are any items, hand one back.
                if (!_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 (_doneWriting != null)
                {
                    return(ChannelUtilities.GetErrorValueTask <T>(_doneWriting));
                }

                // Otherwise, queue the reader.
                var reader = ReaderInteractor <T> .Create(_runContinuationsAsynchronously, cancellationToken);

                _blockedReaders.EnqueueTail(reader);
                return(new ValueTask <T>(reader.Task));
            }
        }
        private ValueTask <T> ReadAsyncCore(CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
            }

            lock (SyncObj)
            {
                // Now that we hold the lock, try reading again.
                T item;
                if (TryRead(out item))
                {
                    return(new ValueTask <T>(item));
                }

                // If no more items will be written, fail the read.
                if (_doneWriting != null)
                {
                    return(ChannelUtilities.GetInvalidCompletionValueTask <T>(_doneWriting));
                }

                Debug.Assert(_blockedReader == null || ((_blockedReader as ReaderInteractor <T>)?.Task.IsCanceled ?? false),
                             "Incorrect usage; multiple outstanding reads were issued against this single-consumer channel");

                // Store the reader to be completed by a writer.
                ReaderInteractor <T> reader = ReaderInteractor <T> .Create(_runContinuationsAsynchronously, cancellationToken);

                _blockedReader = reader;
                return(new ValueTask <T>(reader.Task));
            }
        }
Exemple #3
0
        private ValueTask <T> ReadAsyncCore(CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
            }

            lock (SyncObj)
            {
                AssertInvariants();

                // If we're already completed, nothing to read.
                if (_completion.Task.IsCompleted)
                {
                    return(new ValueTask <T>(
                               _completion.Task.IsCanceled ? Task.FromCanceled <T>(new CancellationToken(true)) :
                               Task.FromException <T>(
                                   _completion.Task.IsFaulted ?
                                   ChannelUtilities.CreateInvalidCompletionException(_completion.Task.Exception.InnerException) :
                                   ChannelUtilities.CreateInvalidCompletionException())));
                }

                // If there are any blocked writers, find one to pair up with
                // and get its data.  Writers that got canceled will remain in the queue,
                // so we need to loop to skip past them.
                while (!_blockedWriters.IsEmpty)
                {
                    WriterInteractor <T> w = _blockedWriters.DequeueHead();
                    if (w.Success(default(VoidResult)))
                    {
                        return(new ValueTask <T>(w.Item));
                    }
                }

                // No writer found to pair with.  Queue the reader.
                var r = ReaderInteractor <T> .Create(true, cancellationToken);

                _blockedReaders.EnqueueTail(r);

                // And let any waiting writers know it's their lucky day.
                ChannelUtilities.WakeUpWaiters(ref _waitingWriters, result: true);

                return(new ValueTask <T>(r.Task));
            }
        }
Exemple #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);
        }
        private Task <bool> WaitToReadAsync(CancellationToken cancellationToken = default)
        {
            // Outside of the lock, check if there are any items waiting to be read.  If there are, we're done.
            if (!_items.IsEmpty)
            {
                return(ChannelUtilities.TrueTask);
            }

            // Now check for cancellation.
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <bool>(cancellationToken));
            }

            ReaderInteractor <bool> oldWaiter = null, newWaiter;

            lock (SyncObj)
            {
                // Again while holding the lock, check to see if there are any items available.
                if (!_items.IsEmpty)
                {
                    return(ChannelUtilities.TrueTask);
                }

                // There aren't any items; if we're done writing, there never will be more items.
                if (_doneWriting != null)
                {
                    return(_doneWriting != ChannelUtilities.DoneWritingSentinel ?
                           Task.FromException <bool>(_doneWriting) :
                           ChannelUtilities.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      = _waitingReader;
                _waitingReader = newWaiter = ReaderInteractor <bool> .Create(_runContinuationsAsynchronously, cancellationToken);
            }

            oldWaiter?.TrySetCanceled();
            return(newWaiter.Task);
        }
Exemple #6
0
        private ValueTask <T> ReadAsyncCore(CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
            }

            lock (SyncObj)
            {
                AssertInvariants();

                // If there are any items, return one.
                T item;
                if (_items.TryDequeue(out item))
                {
                    // Dequeue an item
                    if (_doneWriting != null && _items.IsEmpty)
                    {
                        // If we've now emptied the items queue and we're not getting any more, complete.
                        ChannelUtilities.Complete(_completion, _doneWriting);
                    }

                    return(new ValueTask <T>(item));
                }

                // There are no items, so if we're done writing, fail.
                if (_doneWriting != null)
                {
                    return(ChannelUtilities.GetInvalidCompletionValueTask <T>(_doneWriting));
                }

                // Otherwise, queue the reader.
                ReaderInteractor <T> reader = ReaderInteractor <T> .Create(_runContinuationsAsynchronously, cancellationToken);

                _blockedReaders.EnqueueTail(reader);
                return(new ValueTask <T>(reader.Task));
            }
        }