Ejemplo n.º 1
0
        /// <summary>Asynchronously writes an item to the channel.</summary>
        /// <param name="item">The value to write to the channel.</param>
        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the write operation.</param>
        /// <returns>A <see cref="Task"/> that represents the asynchronous write operation.</returns>
        public virtual Task WriteAsync(T item, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                return
                    (cancellationToken.IsCancellationRequested ? Task.FromCanceled <T>(cancellationToken) :
                     TryWrite(item) ? Task.CompletedTask :
                     WriteAsyncCore(item, cancellationToken));
            }
            catch (Exception e)
            {
                return(Task.FromException(e));
            }

            async Task WriteAsyncCore(T innerItem, CancellationToken ct)
            {
                while (await WaitToWriteAsync(ct).ConfigureAwait(false))
                {
                    if (TryWrite(innerItem))
                    {
                        return;
                    }
                }

                throw ChannelUtilities.CreateInvalidCompletionException();
            }
        }
Ejemplo n.º 2
0
 /// <summary>Mark the channel as being complete, meaning no more items will be written to it.</summary>
 /// <param name="error">Optional Exception indicating a failure that's causing the channel to complete.</param>
 /// <exception cref="InvalidOperationException">The channel has already been marked as complete.</exception>
 public void Complete(Exception error = null)
 {
     if (!TryComplete(error))
     {
         throw ChannelUtilities.CreateInvalidCompletionException();
     }
 }
Ejemplo n.º 3
0
        private async Task WriteAsyncCore(T innerItem, CancellationToken ct)
        {
            while (await WaitToWriteAsync(ct).ConfigureAwait(false))
            {
                if (TryWrite(innerItem))
                {
                    return;
                }
            }

            throw ChannelUtilities.CreateInvalidCompletionException();
        }
Ejemplo n.º 4
0
 public override ValueTask WriteAsync(T item, CancellationToken cancellationToken) =>
 cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) :
 TryWrite(item) ? default :
 new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)));
Ejemplo n.º 5
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);
            }
Ejemplo n.º 6
0
 public override ValueTask WriteAsync(T item, CancellationToken cancellationToken) =>
 // Writing always succeeds (unless we've already completed writing or cancellation has been requested),
 // so just TryWrite and return a completed task.
 cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) :
 TryWrite(item) ? default :
 new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)));
Ejemplo n.º 7
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);
            }