Ejemplo n.º 1
0
            public override ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken)
            {
                // Outside of the lock, check if there are any items waiting to be read.  If there are, we're done.
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <bool>(Task.FromCanceled <bool>(cancellationToken)));
                }

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

                SingleConsumerUnboundedChannel <T> parent = _parent;
                AsyncOperation <bool> oldWaitingReader = null, newWaitingReader;

                lock (parent.SyncObj)
                {
                    // Again while holding the lock, check to see if there are any items available.
                    if (!parent._items.IsEmpty)
                    {
                        return(new ValueTask <bool>(true));
                    }

                    // There aren't any items; if we're done writing, there never will be more items.
                    if (parent._doneWriting != null)
                    {
                        return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ?
                               new ValueTask <bool>(Task.FromException <bool>(parent._doneWriting)) :
                               new ValueTask <bool>(false));
                    }

                    // Try to use the singleton waiter.  If it's currently being used, then the channel
                    // is being used erroneously, and we cancel the outstanding operation.
                    oldWaitingReader = parent._waitingReader;
                    if (!cancellationToken.CanBeCanceled && _waiterSingleton.TryOwnAndReset())
                    {
                        newWaitingReader = _waiterSingleton;
                        if (newWaitingReader == oldWaitingReader)
                        {
                            // The previous operation completed, so null out the "old" waiter
                            // so we don't end up canceling the new operation.
                            oldWaitingReader = null;
                        }
                    }
                    else
                    {
                        newWaitingReader = new AsyncOperation <bool>(_parent._runContinuationsAsynchronously, cancellationToken);
                    }
                    parent._waitingReader = newWaitingReader;
                }

                oldWaitingReader?.TrySetCanceled();
                return(newWaitingReader.ValueTaskOfT);
            }
Ejemplo n.º 2
0
            public override ValueTask <T> ReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
                }

                if (TryRead(out T item))
                {
                    return(new ValueTask <T>(item));
                }

                SingleConsumerUnboundedChannel <T> parent = _parent;

                AsyncOperation <T> oldBlockedReader, newBlockedReader;

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

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

                    // Try to use the singleton reader.  If it's currently being used, then the channel
                    // is being used erroneously, and we cancel the outstanding operation.
                    oldBlockedReader = parent._blockedReader;
                    if (!cancellationToken.CanBeCanceled && _readerSingleton.TryOwnAndReset())
                    {
                        newBlockedReader = _readerSingleton;
                        if (newBlockedReader == oldBlockedReader)
                        {
                            // The previous operation completed, so null out the "old" reader
                            // so we don't end up canceling the new operation.
                            oldBlockedReader = null;
                        }
                    }
                    else
                    {
                        newBlockedReader = new AsyncOperation <T>(_parent._runContinuationsAsynchronously, cancellationToken);
                    }
                    parent._blockedReader = newBlockedReader;
                }

                oldBlockedReader?.TrySetCanceled();
                return(newBlockedReader.ValueTaskOfT);
            }
Ejemplo n.º 3
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);
                }
            }
Ejemplo n.º 4
0
            public override ValueTask <T> ReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
                }

                BoundedChannel <T> parent = _parent;

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

                    // If there are any items, hand one back.
                    if (!parent._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 (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, queue a reader.  Note that in addition to checking whether synchronous continuations were requested,
                    // we also check whether the supplied cancellation token can be canceled.  The writer calls UnregisterCancellation
                    // while holding the lock, and if a callback needs to be unregistered and is currently running, it needs to wait
                    // for that callback to complete so that the subsequent code knows it won't be contending with another thread
                    // trying to complete the operation.  However, if we allowed a synchronous continuation from this operation, that
                    // cancellation callback could end up running arbitrary code, including code that called back into the reader or
                    // writer and tried to take the same lock held by the thread running UnregisterCancellation... deadlock.  As such,
                    // we only allow synchronous continuations here if both a) the caller requested it and the token isn't cancelable.
                    var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously | cancellationToken.CanBeCanceled, cancellationToken);
                    parent._blockedReaders.EnqueueTail(reader);
                    return(reader.ValueTaskOfT);
                }
            }
Ejemplo n.º 5
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);
                }
            }
Ejemplo n.º 6
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);
                }
            }
Ejemplo n.º 7
0
            public override ValueTask <bool> WaitToReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <bool>(Task.FromCanceled <bool>(cancellationToken)));
                }

                BoundedChannel <T> parent = _parent;

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

                    // If there are any items available, a read is possible.
                    if (!parent._items.IsEmpty)
                    {
                        return(new ValueTask <bool>(true));
                    }

                    // There were no items available, so if we're done writing, a read will never be possible.
                    if (parent._doneWriting != null)
                    {
                        return(parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ?
                               new ValueTask <bool>(Task.FromException <bool>(parent._doneWriting)) :
                               new ValueTask <bool>(false));
                    }

                    // There were no items available, but there could be in the future, so ensure
                    // there's a blocked reader task and return it.

                    // 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, queue a reader.
                    var waiter = new AsyncOperation <bool>(parent._runContinuationsAsynchronously, cancellationToken);
                    ChannelUtilities.QueueWaiter(ref _parent._waitingReadersTail, waiter);
                    return(waiter.ValueTaskOfT);
                }
            }
Ejemplo n.º 8
0
            public override ValueTask <T> ReadAsync(CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ValueTask <T>(Task.FromCanceled <T>(cancellationToken)));
                }

                BoundedChannel <T> parent = _parent;

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

                    // If there are any items, hand one back.
                    if (!parent._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 (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, queue the reader.
                    var reader = new AsyncOperation <T>(parent._runContinuationsAsynchronously, cancellationToken);
                    parent._blockedReaders.EnqueueTail(reader);
                    return(reader.ValueTaskOfT);
                }
            }