Esempio n. 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()
            {
                BoundedChannel <T> parent = _parent;

                Debug.Assert(Monitor.IsEntered(parent.SyncObj));

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

                if (parent._doneWriting != null)
                {
                    // We're done writing, so if we're now empty, complete the channel.
                    if (parent._items.IsEmpty)
                    {
                        ChannelUtilities.Complete(parent._completion, parent._doneWriting);
                    }
                }
                else
                {
                    // 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).
                    //
                    // (It's possible for _doneWriting to be non-null due to Complete
                    // having been called but for there to still be blocked/waiting writers.
                    // This is a temporary condition, after which Complete has set _doneWriting
                    // and then exited the lock; at that point it'll proceed to clean this up,
                    // so we just ignore them.)

                    while (!parent._blockedWriters.IsEmpty)
                    {
                        WriterInteractor <T> w = parent._blockedWriters.DequeueHead();
                        if (w.Success(default))
Esempio n. 2
0
 private void CompleteIfDone(UnboundedChannel <T> parent)
 {
     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);
     }
 }
Esempio n. 3
0
 public override bool TryRead(out T item)
 {
     SingleConsumerUnboundedChannel<T> parent = _parent;
     if (parent._items.TryDequeue(out item))
     {
         if (parent._doneWriting != null && parent._items.IsEmpty)
         {
             ChannelUtilities.Complete(parent._completion, parent._doneWriting);
         }
         return true;
     }
     return false;
 }
Esempio n. 4
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));
                    }

                    // If we're able to use the singleton reader, do so.
                    if (!cancellationToken.CanBeCanceled)
                    {
                        AsyncOperation <T> singleton = _readerSingleton;
                        if (singleton.TryOwnAndReset())
                        {
                            parent._blockedReaders.EnqueueTail(singleton);
                            return(singleton.ValueTaskOfT);
                        }
                    }

                    // Otherwise, create and queue a reader.
                    var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously, cancellationToken);
                    parent._blockedReaders.EnqueueTail(reader);
                    return(reader.ValueTaskOfT);
                }
            }
Esempio n. 5
0
            public override bool TryRead(out T item)
            {
                UnboundedChannel <T> parent = _parent;

                // Dequeue an item if we can
                if (parent._items.TryDequeue(out 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(true);
                }

                item = default;
                return(false);
            }
Esempio n. 6
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));
                }
            }
Esempio n. 7
0
            public override bool TryComplete(Exception error)
            {
                UnboundedChannel <T> parent = _parent;
                bool completeTask;

                lock (parent.SyncObj)
                {
                    parent.AssertInvariants();

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

                    // Mark that we're done writing.
                    parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;
                    completeTask        = parent._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(parent._completion, error);
                }

                // At this point, _blockedReaders and _waitingReaders will not be mutated:
                // they're only mutated by readers while holding the lock, and only if _doneWriting is null.
                // freely manipulate _blockedReaders and _waitingReaders without any concurrency concerns.
                ChannelUtilities.FailOperations <AsyncOperation <T>, T>(parent._blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error));
                ChannelUtilities.WakeUpWaiters(ref parent._waitingReadersTail, result: false, error: error);

                // Successfully transitioned to completed.
                return(true);
            }
Esempio n. 8
0
            /// <summary>Dequeues an item, and then fixes up our state around writers and completion.</summary>
            /// <returns>The dequeued item.</returns>
            private T DequeueItemAndPostProcess()
            {
                BoundedChannel <T> parent = _parent;

                Debug.Assert(Monitor.IsEntered(parent.SyncObj));

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

                // If we're now empty and we're done writing, complete the channel.
                if (parent._doneWriting != null && parent._items.IsEmpty)
                {
                    ChannelUtilities.Complete(parent._completion, parent._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 (!parent._blockedWriters.IsEmpty)
                {
                    WriterInteractor <T> w = parent._blockedWriters.DequeueHead();
                    if (w.Success(default(VoidResult)))
                    {
                        parent._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 parent._waitingWriters, result: true);

                // Return the item
                return(item);
            }
Esempio n. 9
0
            /// <summary>Dequeues an item, and then fixes up our state around writers and completion.</summary>
            /// <returns>The dequeued item.</returns>
            private T DequeueItemAndPostProcess()
            {
                BoundedChannel <T> parent = _parent;

                Debug.Assert(Monitor.IsEntered(parent.SyncObj));

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

                // If we're now empty and we're done writing, complete the channel.
                if (parent._doneWriting != null && parent._items.IsEmpty)
                {
                    ChannelUtilities.Complete(parent._completion, parent._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 (!parent._blockedWriters.IsEmpty)
                {
                    WriterInteractor <T> w = parent._blockedWriters.DequeueHead();
                    if (w.Success(default))
Esempio n. 10
0
            public override bool TryComplete(Exception error)
            {
                AsyncOperation <T>    blockedReader = null;
                AsyncOperation <bool> waitingReader = null;
                bool completeTask = false;

                SingleConsumerUnboundedChannel <T> parent = _parent;

                lock (parent.SyncObj)
                {
                    // If we're already marked as complete, there's nothing more to do.
                    if (parent._doneWriting != null)
                    {
                        return(false);
                    }

                    // Mark as complete for writing.
                    parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;

                    // If we have no more items remaining, then the channel needs to be marked as completed
                    // and readers need to be informed they'll never get another item.  All of that needs
                    // to happen outside of the lock to avoid invoking continuations under the lock.
                    if (parent._items.IsEmpty)
                    {
                        completeTask = true;

                        if (parent._blockedReader != null)
                        {
                            blockedReader         = parent._blockedReader;
                            parent._blockedReader = null;
                        }

                        if (parent._waitingReader != null)
                        {
                            waitingReader         = parent._waitingReader;
                            parent._waitingReader = null;
                        }
                    }
                }

                // Complete the channel task if necessary
                if (completeTask)
                {
                    ChannelUtilities.Complete(parent._completion, error);
                }

                Debug.Assert(blockedReader == null || waitingReader == null, "There should only ever be at most one reader.");

                // Complete a blocked reader if necessary
                if (blockedReader != null)
                {
                    error = ChannelUtilities.CreateInvalidCompletionException(error);
                    blockedReader.TrySetException(error);
                }

                // Complete a waiting reader if necessary.  (We really shouldn't have both a blockedReader
                // and a waitingReader, but it's more expensive to prevent it than to just tolerate it.)
                if (waitingReader != null)
                {
                    if (error != null)
                    {
                        waitingReader.TrySetException(error);
                    }
                    else
                    {
                        waitingReader.TrySetResult(item: false);
                    }
                }

                // Successfully completed the channel
                return(true);
            }