Example #1
0
        internal ValueTask <FlushResult> FlushAsync(CancellationToken cancellationToken)
        {
            CompletionData completionData;
            CancellationTokenRegistration cancellationTokenRegistration;
            ValueTask <FlushResult>       result;

            lock (_sync)
            {
                var wasEmpty = CommitUnsynchronized();

                // AttachToken before completing reader awaiter in case cancellationToken is already completed
                cancellationTokenRegistration = _writerAwaitable.BeginOperation(cancellationToken, s_signalWriterAwaitable, this);

                // If the writer is completed (which it will be most of the time) then return a completed ValueTask
                if (_writerAwaitable.IsCompleted)
                {
                    var flushResult = new FlushResult();
                    GetFlushResult(ref flushResult);
                    result = new ValueTask <FlushResult>(flushResult);
                }
                else
                {
                    // Otherwise it's async
                    result = new ValueTask <FlushResult>(_writer, token: 0);
                }

                // Complete reader only if new data was pushed into the pipe
                // Avoid throwing in between completing the reader and scheduling the callback
                // if the intent is to allow pipe to continue reading the data
                if (!wasEmpty)
                {
                    _readerAwaitable.Complete(out completionData);
                }
                else
                {
                    completionData = default;
                }

                // I couldn't find a way for flush to induce backpressure deadlock
                // if it always adds new data to pipe and wakes up the reader but assert anyway
                Debug.Assert(_writerAwaitable.IsCompleted || _readerAwaitable.IsCompleted);
            }

            cancellationTokenRegistration.Dispose();

            TrySchedule(_readerScheduler, completionData);

            return(result);
        }
Example #2
0
        private void PrepareFlushUnsynchronized(out CompletionData completionData, out ValueTask <FlushResult> result, CancellationToken cancellationToken)
        {
            var completeReader = CommitUnsynchronized();

            // AttachToken before completing reader awaiter in case cancellationToken is already completed
            _writerAwaitable.BeginOperation(cancellationToken, s_signalWriterAwaitable, this);

            // If the writer is completed (which it will be most of the time) then return a completed ValueTask
            if (_writerAwaitable.IsCompleted)
            {
                FlushResult flushResult = default;
                GetFlushResult(ref flushResult);
                result = new ValueTask <FlushResult>(flushResult);
            }
            else
            {
                // Otherwise it's async
                result = new ValueTask <FlushResult>(_writer, token: 0);
            }

            // Complete reader only if new data was pushed into the pipe
            // Avoid throwing in between completing the reader and scheduling the callback
            // if the intent is to allow pipe to continue reading the data
            if (completeReader)
            {
                _readerAwaitable.Complete(out completionData);
            }
            else
            {
                completionData = default;
            }

            // I couldn't find a way for flush to induce backpressure deadlock
            // if it always adds new data to pipe and wakes up the reader but assert anyway
            Debug.Assert(_writerAwaitable.IsCompleted || _readerAwaitable.IsCompleted);
        }