Exemple #1
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);
            }
        }
Exemple #2
0
        private Task WriteAsync(T item, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled(cancellationToken));
            }

            ReaderInteractor <T>    blockedReader  = null;
            ReaderInteractor <bool> waitingReaders = null;

            lock (SyncObj)
            {
                AssertInvariants();

                // If we're done writing, trying to write is an error.
                if (_doneWriting != null)
                {
                    return(Task.FromException(ChannelUtilities.CreateInvalidCompletionException()));
                }

                // Get the number of items in the channel currently.
                int count = _items.Count;

                if (count == 0)
                {
                    // There are no items in the channel, which means we may have blocked/waiting readers.

                    // If there are any blocked readers, find one that's not canceled
                    // and store it to complete outside of the lock, in case it has
                    // continuations that'll run synchronously
                    while (!_blockedReaders.IsEmpty)
                    {
                        ReaderInteractor <T> r = _blockedReaders.DequeueHead();
                        r.UnregisterCancellation(); // ensure that once we grab it, we own its completion
                        if (!r.Task.IsCompleted)
                        {
                            blockedReader = r;
                            break;
                        }
                    }

                    if (blockedReader == null)
                    {
                        // If there wasn't a blocked reader, then store the item. If no one's waiting
                        // to be notified about a 0-to-1 transition, we're done.
                        _items.EnqueueTail(item);
                        waitingReaders = _waitingReaders;
                        if (waitingReaders == null)
                        {
                            return(ChannelUtilities.TrueTask);
                        }
                        _waitingReaders = null;
                    }
                }
                else if (count < _bufferedCapacity)
                {
                    // There's room in the channel.  Since we're not transitioning from 0-to-1 and
                    // since there's room, we can simply store the item and exit without having to
                    // worry about blocked/waiting readers.
                    _items.EnqueueTail(item);
                    return(ChannelUtilities.TrueTask);
                }
                else if (_mode == BoundedChannelFullMode.Wait)
                {
                    // The channel is full and we're in a wait mode.
                    // Queue the writer.
                    var writer = WriterInteractor <T> .Create(true, cancellationToken, item);

                    _blockedWriters.EnqueueTail(writer);
                    return(writer.Task);
                }
                else
                {
                    // The channel is full, and we're in a dropping mode.
                    // Drop either the oldest or the newest and write the new item.
                    T droppedItem = _mode == BoundedChannelFullMode.DropNewest ?
                                    _items.DequeueTail() :
                                    _items.DequeueHead();
                    _items.EnqueueTail(item);
                    return(ChannelUtilities.TrueTask);
                }
            }

            // We either wrote the item already, or we're transfering it to the blocked reader we grabbed.
            if (blockedReader != null)
            {
                // Transfer the written item to the blocked reader.
                bool success = blockedReader.Success(item);
                Debug.Assert(success, "We should always be able to complete the reader.");
            }
            else
            {
                // We stored an item bringing the count up from 0 to 1.  Alert
                // any waiting readers that there may be something for them to consume.
                // Since we're no longer holding the lock, it's possible we'll end up
                // waking readers that have since come in.
                waitingReaders.Success(item: true);
            }

            return(ChannelUtilities.TrueTask);
        }