Beispiel #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)) :
Beispiel #2
0
            public override bool TryWrite(T item)
            {
                SingleConsumerUnboundedChannel <T> parent = _parent;

                while (true) // in case a reader was canceled and we need to try again
                {
                    AsyncOperation <T>    blockedReader = null;
                    AsyncOperation <bool> waitingReader = null;

                    lock (parent.SyncObj)
                    {
                        // If writing is completed, exit out without writing.
                        if (parent._doneWriting != null)
                        {
                            return(false);
                        }

                        // If there's a blocked reader, store it into a local for completion outside of the lock.
                        // If there isn't a blocked reader, queue the item being written; then if there's a waiting
                        blockedReader = parent._blockedReader;
                        if (blockedReader != null)
                        {
                            parent._blockedReader = null;
                        }
                        else
                        {
                            parent._items.Enqueue(item);

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

                    // If we get here, we grabbed a blocked or a waiting reader.
                    Debug.Assert((blockedReader != null) ^ (waitingReader != null), "Expected either a blocked or waiting reader, but not both");

                    // If we have a waiting reader, notify it that an item was written and exit.
                    if (waitingReader != null)
                    {
                        // If we get here, we grabbed a waiting reader.
                        waitingReader.TrySetResult(item: true);
                        return(true);
                    }

                    // Otherwise we have a blocked reader: complete it with the item being written.
                    // In the case of a ReadAsync(CancellationToken), it's possible the reader could
                    // have been completed due to cancellation by the time we get here.  In that case,
                    // we'll loop around to try again so as not to lose the item being written.
                    Debug.Assert(blockedReader != null);
                    if (blockedReader.TrySetResult(item))
                    {
                        return(true);
                    }
                }
            }
Beispiel #3
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);
            }
Beispiel #4
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);
            }
 internal UnboundedChannelReader(SingleConsumerUnboundedChannel <T> parent)
 {
     _parent          = parent;
     _readerSingleton = new AsyncOperation <T>(parent._runContinuationsAsynchronously)
     {
         UnsafeState = ResettableValueTaskSource.States.Released
     };
     _waiterSingleton = new AsyncOperation <bool>(parent._runContinuationsAsynchronously)
     {
         UnsafeState = ResettableValueTaskSource.States.Released
     };
 }
Beispiel #6
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;
 }
            public override ValueTask <T> ReadAsync(CancellationToken cancellationToken)
            {
                {
                    return(TryRead(out T item) ?
                           new ValueTask <T>(item) :
                           ReadAsyncCore(cancellationToken));
                }

                ValueTask <T> ReadAsyncCore(CancellationToken ct)
                {
                    SingleConsumerUnboundedChannel <T> parent = _parent;

                    if (ct.IsCancellationRequested)
                    {
                        return(new ValueTask <T>(Task.FromCanceled <T>(ct)));
                    }

                    lock (parent.SyncObj)
                    {
                        // Now that we hold the lock, try reading again.
                        if (TryRead(out T 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));
                        }

                        Debug.Assert(parent._blockedReader == null || parent._blockedReader.Task.IsCanceled,
                                     "Incorrect usage; multiple outstanding reads were issued against this single-consumer channel");

                        // Store the reader to be completed by a writer.
                        var reader = ReaderInteractor <T> .Create(parent._runContinuationsAsynchronously, ct);

                        parent._blockedReader = reader;
                        return(new ValueTask <T>(reader.Task));
                    }
                }
            }
            public override Task <bool> WaitToReadAsync(CancellationToken cancellationToken)
            {
                // Outside of the lock, check if there are any items waiting to be read.  If there are, we're done.
                return
                    (cancellationToken.IsCancellationRequested ? Task.FromCanceled <bool>(cancellationToken) :
                     !_parent._items.IsEmpty ? ChannelUtilities.s_trueTask :
                     WaitToReadAsyncCore(cancellationToken));

                Task <bool> WaitToReadAsyncCore(CancellationToken ct)
                {
                    SingleConsumerUnboundedChannel <T> parent = _parent;
                    ReaderInteractor <bool>            oldWaiter = null, newWaiter;

                    lock (parent.SyncObj)
                    {
                        // Again while holding the lock, check to see if there are any items available.
                        if (!parent._items.IsEmpty)
                        {
                            return(ChannelUtilities.s_trueTask);
                        }

                        // 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 ?
                                   Task.FromException <bool>(parent._doneWriting) :
                                   ChannelUtilities.s_falseTask);
                        }

                        // Create the new waiter.  We're a bit more tolerant of a stray waiting reader
                        // than we are of a blocked reader, as with usage patterns it's easier to leave one
                        // behind, so we just cancel any that may have been waiting around.
                        oldWaiter             = parent._waitingReader;
                        parent._waitingReader = newWaiter = ReaderInteractor <bool> .Create(parent._runContinuationsAsynchronously, ct);
                    }

                    oldWaiter?.TrySetCanceled();
                    return(newWaiter.Task);
                }
            }
 internal UnboundedChannelReader(SingleConsumerUnboundedChannel <T> parent)
 {
     _parent          = parent;
     _readerSingleton = new AsyncOperation <T>(parent._runContinuationsAsynchronously, pooled: true);
     _waiterSingleton = new AsyncOperation <bool>(parent._runContinuationsAsynchronously, pooled: true);
 }
Beispiel #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);
            }
Beispiel #11
0
 internal UnboundedChannelWriter(SingleConsumerUnboundedChannel <T> parent) => _parent = parent;