Beispiel #1
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.Success(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);
                    }
                }
            }
            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.Success(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.Success(item))
                    {
                        return(true);
                    }
                }
            }
            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.Fail(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.Fail(error);
                    }
                    else
                    {
                        waitingReader.Success(item: false);
                    }
                }

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