Example #1
0
        /// <summary>
        /// The caller indicates that the offer is now registered in all places
        /// </summary>
        public void ProbePhaseComplete()
        {
            // If there is no timeout, do nothing
            // If we are already completed, do nothing
            if (m_timeout == Timeout.InfiniteDateTime || IsTaken)
            {
                return;
            }

            // If the timeout has occurred, set the timeout
            else if (m_timeout < DateTime.Now)
            {
                ExpirationCallback();
            }

            // Register the timeout callback
            else
            {
                ExpirationManager.AddExpirationCallback(m_timeout, ExpirationCallback);
            }
        }
Example #2
0
        /// <summary>
        /// Registers a desire to write to the channel
        /// </summary>
        /// <param name="offer">A callback method for offering an item, use null to unconditionally accept</param>
        /// <param name="value">The value to write to the channel.</param>
        /// <param name="timeout">The time to wait for the operation, use zero to return a timeout immediately if no items can be read. Use a negative span to wait forever.</param>
        public async Task WriteAsync(T value, TimeSpan timeout, ITwoPhaseOffer offer = null)
        {
            var wr = new WriterEntry(offer, new TaskCompletionSource <bool>(), timeout.Ticks <= 0 ? Timeout.InfiniteDateTime : DateTime.Now + timeout, value);

            using (await m_asynclock.LockAsync())
            {
                if (m_isRetired)
                {
                    ThreadPool.QueueItem(() => wr.Source.SetException(new RetiredException()));
                    await wr.Source.Task;
                    return;
                }

                m_writerQueue.Add(wr);
                if (!await MatchReadersAndWriters(false, wr.Source.Task))
                {
                    System.Diagnostics.Debug.Assert(m_writerQueue[m_writerQueue.Count - 1].Source == wr.Source);

                    // If we have a buffer slot to use
                    if (m_writerQueue.Count <= m_bufferSize && m_retireCount < 0)
                    {
                        if (offer == null || await offer.OfferAsync(this))
                        {
                            if (offer != null)
                            {
                                await offer.CommitAsync(this);
                            }

                            m_writerQueue[m_writerQueue.Count - 1] = new WriterEntry(null, null, Timeout.InfiniteDateTime, value);
                            wr.Source.TrySetResult(true);
                        }
                        else
                        {
                            wr.Source.TrySetCanceled();
                        }
                    }
                    else
                    {
                        // If this was a probe call, return a timeout now
                        if (timeout.Ticks >= 0 && wr.Expires < DateTime.Now)
                        {
                            m_writerQueue.RemoveAt(m_writerQueue.Count - 1);
                            ThreadPool.QueueItem(() => wr.Source.SetException(new TimeoutException()));
                        }
                        else
                        {
                            // Make room if we have too many
                            if (m_maxPendingWriters > 0 && (m_writerQueue.Count - m_bufferSize - 1) >= m_maxPendingWriters)
                            {
                                switch (m_pendingWritersOverflowStrategy)
                                {
                                case QueueOverflowStrategy.FIFO:
                                {
                                    var exp = m_writerQueue[m_bufferSize].Source;
                                    m_writerQueue.RemoveAt(m_bufferSize);
                                    if (exp != null)
                                    {
                                        ThreadPool.QueueItem(() => exp.TrySetException(new ChannelOverflowException()));
                                    }
                                }

                                break;

                                case QueueOverflowStrategy.LIFO:
                                {
                                    var exp = m_writerQueue[m_writerQueue.Count - 2].Source;
                                    m_writerQueue.RemoveAt(m_writerQueue.Count - 2);
                                    if (exp != null)
                                    {
                                        ThreadPool.QueueItem(() => exp.TrySetException(new ChannelOverflowException()));
                                    }
                                }

                                break;

                                case QueueOverflowStrategy.Reject:
                                default:
                                {
                                    var exp = m_writerQueue[m_writerQueue.Count - 1].Source;
                                    m_writerQueue.RemoveAt(m_writerQueue.Count - 1);
                                    if (exp != null)
                                    {
                                        ThreadPool.QueueItem(() => exp.TrySetException(new ChannelOverflowException()));
                                    }
                                    await wr.Source.Task;
                                }

                                    return;
                                }
                            }

                            // If we have expanded the queue with a new batch, see if we can purge old entries
                            m_writerQueueCleanup = await PerformQueueCleanupAsync(m_writerQueue, true, m_writerQueueCleanup);

                            if (wr.Expires != Timeout.InfiniteDateTime)
                            {
                                ExpirationManager.AddExpirationCallback(wr.Expires, () => ExpireItemsAsync().FireAndForget());
                            }
                        }
                    }
                }
            }

            await wr.Source.Task;

            return;
        }
Example #3
0
        /// <summary>
        /// Registers a desire to read from the channel
        /// </summary>
        /// <param name="offer">A callback method for offering an item, use null to unconditionally accept</param>
        /// <param name="timeout">The time to wait for the operation, use zero to return a timeout immediately if no items can be read. Use a negative span to wait forever.</param>
        public async Task <T> ReadAsync(TimeSpan timeout, ITwoPhaseOffer offer = null)
        {
            var rd = new ReaderEntry(offer, new TaskCompletionSource <T>(), timeout.Ticks <= 0 ? Timeout.InfiniteDateTime : DateTime.Now + timeout);

            using (await m_asynclock.LockAsync())
            {
                if (m_isRetired)
                {
                    ThreadPool.QueueItem(() => rd.Source.SetException(new RetiredException()));
                    return(await rd.Source.Task);
                }

                m_readerQueue.Add(rd);
                if (!await MatchReadersAndWriters(true, rd.Source.Task))
                {
                    System.Diagnostics.Debug.Assert(m_readerQueue[m_readerQueue.Count - 1].Source == rd.Source);

                    // If this was a probe call, return a timeout now
                    if (timeout.Ticks >= 0 && rd.Expires < DateTime.Now)
                    {
                        m_readerQueue.RemoveAt(m_readerQueue.Count - 1);
                        ThreadPool.QueueItem(() => rd.Source.TrySetException(new TimeoutException()));
                    }
                    else
                    {
                        // Make room if we have too many
                        if (m_maxPendingReaders > 0 && (m_readerQueue.Count - 1) >= m_maxPendingReaders)
                        {
                            switch (m_pendingReadersOverflowStrategy)
                            {
                            case QueueOverflowStrategy.FIFO:
                            {
                                var exp = m_readerQueue[0].Source;
                                m_readerQueue.RemoveAt(0);
                                ThreadPool.QueueItem(() => exp.TrySetException(new ChannelOverflowException()));
                            }

                            break;

                            case QueueOverflowStrategy.LIFO:
                            {
                                var exp = m_readerQueue[m_readerQueue.Count - 2].Source;
                                m_readerQueue.RemoveAt(m_readerQueue.Count - 2);
                                ThreadPool.QueueItem(() => exp.TrySetException(new ChannelOverflowException()));
                            }

                            break;

                            case QueueOverflowStrategy.Reject:
                            default:
                            {
                                var exp = m_readerQueue[m_readerQueue.Count - 1].Source;
                                m_readerQueue.RemoveAt(m_readerQueue.Count - 1);
                                ThreadPool.QueueItem(() => exp.TrySetException(new ChannelOverflowException()));

                                await rd.Source.Task;
                            }

                                return(await rd.Source.Task);
                            }
                        }

                        // If we have expanded the queue with a new batch, see if we can purge old entries
                        m_readerQueueCleanup = await PerformQueueCleanupAsync(m_readerQueue, true, m_readerQueueCleanup);

                        if (rd.Expires != Timeout.InfiniteDateTime)
                        {
                            ExpirationManager.AddExpirationCallback(rd.Expires, () => ExpireItemsAsync().FireAndForget());
                        }
                    }
                }
            }

            return(await rd.Source.Task);
        }