예제 #1
0
        /// <summary>Dequeues an item, and then fixes up our state around writers and completion.</summary>
        /// <returns>The dequeued item.</returns>
        private T DequeueItemAndPostProcess()
        {
            Debug.Assert(Monitor.IsEntered(SyncObj));

            // Dequeue an item.
            T item = _items.DequeueHead();

            // If we're now empty and we're done writing, complete the channel.
            if (_doneWriting != null && _items.IsEmpty)
            {
                ChannelUtilities.Complete(_completion, _doneWriting);
            }

            // If there are any writers blocked, there's now room for at least one
            // to be promoted to have its item moved into the items queue.  We need
            // to loop while trying to complete the writer in order to find one that
            // hasn't yet been canceled (canceled writers transition to canceled but
            // remain in the physical queue).
            while (!_blockedWriters.IsEmpty)
            {
                WriterInteractor <T> w = _blockedWriters.DequeueHead();
                if (w.Success(default(VoidResult)))
                {
                    _items.EnqueueTail(w.Item);
                    return(item);
                }
            }

            // There was no blocked writer, so see if there's a WaitToWriteAsync
            // we should wake up.
            ChannelUtilities.WakeUpWaiters(ref _waitingWriters, result: true);

            // Return the item
            return(item);
        }
예제 #2
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));
            }
        }
예제 #3
0
        private bool TryComplete(Exception error = null)
        {
            bool completeTask;

            lock (SyncObj)
            {
                AssertInvariants();

                // If we've already marked the channel as completed, bail.
                if (_doneWriting != null)
                {
                    return(false);
                }

                // Mark that we're done writing.
                _doneWriting = error ?? ChannelUtilities.DoneWritingSentinel;
                completeTask = _items.IsEmpty;
            }

            // If there are no items in the queue, complete the channel's task,
            // as no more data can possibly arrive at this point.  We do this outside
            // of the lock in case we'll be running synchronous completions, and we
            // do it before completing blocked/waiting readers, so that when they
            // wake up they'll see the task as being completed.
            if (completeTask)
            {
                ChannelUtilities.Complete(_completion, error);
            }

            // At this point, _blockedReaders/Writers and _waitingReaders/Writers will not be mutated:
            // they're only mutated by readers/writers while holding the lock, and only if _doneWriting is null.
            // We also know that only one thread (this one) will ever get here, as only that thread
            // will be the one to transition from _doneWriting false to true.  As such, we can
            // freely manipulate _blockedReaders and _waitingReaders without any concurrency concerns.
            ChannelUtilities.FailInteractors <ReaderInteractor <T>, T>(_blockedReaders, error);
            ChannelUtilities.FailInteractors <WriterInteractor <T>, VoidResult>(_blockedWriters, error);
            ChannelUtilities.WakeUpWaiters(ref _waitingReaders, result: false);
            ChannelUtilities.WakeUpWaiters(ref _waitingWriters, result: false);

            // Successfully transitioned to completed.
            return(true);
        }
예제 #4
0
        private Task WriteAsync(T item, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled(cancellationToken));
            }

            lock (SyncObj)
            {
                // Fail if we've already completed
                if (_completion.Task.IsCompleted)
                {
                    return
                        (_completion.Task.IsCanceled ? Task.FromCanceled <T>(new CancellationToken(true)) :
                         Task.FromException <T>(
                             _completion.Task.IsFaulted ?
                             ChannelUtilities.CreateInvalidCompletionException(_completion.Task.Exception.InnerException) :
                             ChannelUtilities.CreateInvalidCompletionException()));
                }

                // Try to find a reader to pair with.  Canceled readers remain in the queue,
                // so we need to loop until we find one.
                while (!_blockedReaders.IsEmpty)
                {
                    ReaderInteractor <T> r = _blockedReaders.DequeueHead();
                    if (r.Success(item))
                    {
                        return(Task.CompletedTask);
                    }
                }

                // No reader was available.  Queue the writer.
                var w = WriterInteractor <T> .Create(true, cancellationToken, item);

                _blockedWriters.EnqueueTail(w);

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

                return(w.Task);
            }
        }
예제 #5
0
        private bool TryComplete(Exception error = null)
        {
            lock (SyncObj)
            {
                AssertInvariants();

                // Mark the channel as being done. Since there's no buffered data, we can complete immediately.
                if (_completion.Task.IsCompleted)
                {
                    return(false);
                }
                ChannelUtilities.Complete(_completion, error);

                // Fail any blocked readers/writers, as there will be no writers/readers to pair them with.
                ChannelUtilities.FailInteractors <ReaderInteractor <T>, T>(_blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error));
                ChannelUtilities.FailInteractors <WriterInteractor <T>, VoidResult>(_blockedWriters, ChannelUtilities.CreateInvalidCompletionException(error));

                // Let any waiting readers and writers know there won't be any more data
                ChannelUtilities.WakeUpWaiters(ref _waitingReaders, result: false, error: error);
                ChannelUtilities.WakeUpWaiters(ref _waitingWriters, result: false, error: error);
            }

            return(true);
        }