Example #1
0
            public override ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <bool>(Task.FromCanceled <bool>(cancellationToken)));
                }

                if (!_parent._items.IsEmpty)
                {
                    return(new ValueTask <bool>(true));
                }

                UnboundedChannel <T> parent = _parent;

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

                    // Try again to read now that we're synchronized with writers.
                    if (!parent._items.IsEmpty)
                    {
                        return(new ValueTask <bool>(true));
                    }

                    // There are no items, so if we're done writing, there's never going to be data available.
                    if (parent._doneWriting != null)
                    {
                        return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ?
                               new ValueTask <bool>(Task.FromException <bool>(parent._doneWriting)) :
Example #2
0
            public override bool TryWrite(T item)
            {
                UnboundedChannel <T> parent = _parent;

                while (true)
                {
                    AsyncOperation <T>    blockedReader      = null;
                    AsyncOperation <bool> waitingReadersTail = null;
                    lock (parent.SyncObj)
                    {
                        // If writing has already been marked as done, fail the write.
                        parent.AssertInvariants();
                        if (parent._doneWriting != null)
                        {
                            return(false);
                        }

                        // If there aren't any blocked readers, just add the data to the queue,
                        // and let any waiting readers know that they should try to read it.
                        // We can only complete such waiters here under the lock if they run
                        // continuations asynchronously (otherwise the synchronous continuations
                        // could be invoked under the lock).  If we don't complete them here, we
                        // need to do so outside of the lock.
                        if (parent._blockedReaders.IsEmpty)
                        {
                            parent._items.Enqueue(item);
                            waitingReadersTail = parent._waitingReadersTail;
                            if (waitingReadersTail == null)
                            {
                                return(true);
                            }
                            parent._waitingReadersTail = null;
                        }
                        else
                        {
                            // There were blocked readers.  Grab one, and then complete it outside of the lock.
                            blockedReader = parent._blockedReaders.DequeueHead();
                        }
                    }

                    if (blockedReader != null)
                    {
                        // Complete the reader.  It's possible the reader was canceled, in which
                        // case we loop around to try everything again.
                        if (blockedReader.TrySetResult(item))
                        {
                            return(true);
                        }
                    }
                    else
                    {
                        // Wake up all of the waiters.  Since we've released the lock, it's possible
                        // we could cause some spurious wake-ups here, if we tell a waiter there's
                        // something available but all data has already been removed.  It's a benign
                        // race condition, though, as consumers already need to account for such things.
                        ChannelUtilities.WakeUpWaiters(ref waitingReadersTail, result: true);
                        return(true);
                    }
                }
            }
Example #3
0
            public override Task <bool> WaitToReadAsync(CancellationToken cancellationToken)
            {
                return
                    (cancellationToken.IsCancellationRequested ? Task.FromCanceled <bool>(cancellationToken) :
                     !_parent._items.IsEmpty ? ChannelUtilities.s_trueTask :
                     WaitToReadAsyncCore(cancellationToken));

                Task <bool> WaitToReadAsyncCore(CancellationToken ct)
                {
                    UnboundedChannel <T> parent = _parent;

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

                        // Try again to read now that we're synchronized with writers.
                        if (!parent._items.IsEmpty)
                        {
                            return(ChannelUtilities.s_trueTask);
                        }

                        // There are no items, so if we're done writing, there's never going to be data available.
                        if (parent._doneWriting != null)
                        {
                            return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ?
                                   Task.FromException <bool>(parent._doneWriting) :
                                   ChannelUtilities.s_falseTask);
                        }

                        // Queue the waiter
                        return(ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, parent._runContinuationsAsynchronously, ct));
                    }
                }
            }
Example #4
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);
     }
 }
Example #5
0
            public override bool TryRead([MaybeNullWhen(false)] out T item)
            {
                UnboundedChannel <T> parent = _parent;

                // Dequeue an item if we can
                if (parent._items.TryDequeue(out item))
                {
                    CompleteIfDone(parent);
                    return(true);
                }

                item = default !;
Example #6
0
 internal UnboundedChannelReader(UnboundedChannel <T> parent)
 {
     _parent          = parent;
     _readerSingleton = new AsyncOperation <T>(parent._runContinuationsAsynchronously)
     {
         UnsafeState = ResettableValueTaskSource.States.Released
     };
     _waiterSingleton = new AsyncOperation <bool>(parent._runContinuationsAsynchronously)
     {
         UnsafeState = ResettableValueTaskSource.States.Released
     };
 }
Example #7
0
            public override ValueTask <T> ReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
                }

                // Dequeue an item if we can.
                UnboundedChannel <T> parent = _parent;

                if (parent._items.TryDequeue(out T item))
                {
                    CompleteIfDone(parent);
                    return(new ValueTask <T>(item));
                }

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

                    // Try to dequeue again, now that we hold the lock.
                    if (parent._items.TryDequeue(out item))
                    {
                        CompleteIfDone(parent);
                        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);
                }
            }
Example #8
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);
                }
            }
Example #9
0
            public override ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <bool>(Task.FromCanceled <bool>(cancellationToken)));
                }

                if (!_parent._items.IsEmpty)
                {
                    return(new ValueTask <bool>(true));
                }

                UnboundedChannel <T> parent = _parent;

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

                    // Try again to read now that we're synchronized with writers.
                    if (!parent._items.IsEmpty)
                    {
                        return(new ValueTask <bool>(true));
                    }

                    // There are no items, so if we're done writing, there's never going to be data available.
                    if (parent._doneWriting != null)
                    {
                        return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ?
                               new ValueTask <bool>(Task.FromException <bool>(parent._doneWriting)) :
                               new ValueTask <bool>(false));
                    }

                    // If we're able to use the singleton waiter, do so.
                    if (!cancellationToken.CanBeCanceled)
                    {
                        AsyncOperation <bool> singleton = _waiterSingleton;
                        if (singleton.TryOwnAndReset())
                        {
                            ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, singleton);
                            return(singleton.ValueTaskOfT);
                        }
                    }

                    // Otherwise, create and queue a waiter.
                    var waiter = new AsyncOperation <bool>(parent._runContinuationsAsynchronously, cancellationToken);
                    ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, waiter);
                    return(waiter.ValueTaskOfT);
                }
            }
Example #10
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);
            }
Example #11
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));
                }
            }
Example #12
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);
            }
Example #13
0
 internal UnboundedChannelReader(UnboundedChannel <T> parent)
 {
     _parent          = parent;
     _readerSingleton = new AsyncOperation <T>(parent._runContinuationsAsynchronously, pooled: true);
     _waiterSingleton = new AsyncOperation <bool>(parent._runContinuationsAsynchronously, pooled: true);
 }
Example #14
0
 internal UnboundedChannelReader(UnboundedChannel <T> parent) => _parent = parent;