private async Task FlushAsyncAwaited(ValueTask <FlushResult> awaitable, long count, CancellationToken cancellationToken)
        {
            // https://github.com/dotnet/corefxlab/issues/1334
            // Since the flush awaitable doesn't currently support multiple awaiters
            // we need to use a task to track the callbacks.
            // All awaiters get the same task
            lock (_flushLock)
            {
                _flushTask = awaitable;
                if (_flushTcs == null || _flushTcs.Task.IsCompleted)
                {
                    _flushTcs = new TaskCompletionSource <object>();

                    _flushTask.GetAwaiter().OnCompleted(_flushCompleted);
                }
            }

            _timeoutControl.StartTimingWrite(count);
            try
            {
                await _flushTcs.Task;
                cancellationToken.ThrowIfCancellationRequested();
            }
            catch (OperationCanceledException)
            {
                _completed = true;
                throw;
            }
            finally
            {
                _timeoutControl.StopTimingWrite();
            }
        }
Exemplo n.º 2
0
    public static void StartDrainTimeout(this ITimeoutControl timeoutControl, MinDataRate?minDataRate, long?maxResponseBufferSize)
    {
        // If maxResponseBufferSize has no value, there's no backpressure and we can't reasonably time out draining.
        if (minDataRate == null || maxResponseBufferSize == null)
        {
            return;
        }

        // Ensure we have at least the grace period from this point to finish draining the response.
        timeoutControl.BytesWrittenToBuffer(minDataRate, 1);
        timeoutControl.StartTimingWrite();
    }
Exemplo n.º 3
0
        public void Dispose()
        {
            lock (_contextLock)
            {
                if (_completed)
                {
                    return;
                }

                _log.ConnectionDisconnect(_connectionId);
                _completed = true;
                _pipeWriter.Complete();

                var unsentBytes = _totalBytesCommitted - _transportBytesWrittenFeature.TotalBytesWritten;

                if (unsentBytes > 0)
                {
                    // unsentBytes should never be over 64KB in the default configuration.
                    _timeoutControl.StartTimingWrite((int)Math.Min(unsentBytes, int.MaxValue));
                    _pipeWriter.OnReaderCompleted((ex, state) => ((ITimeoutControl)state).StopTimingWrite(), _timeoutControl);
                }
            }
        }
Exemplo n.º 4
0
        public static void StartDrainTimeout(this ITimeoutControl timeoutControl, MinDataRate minDataRate, long?maxResponseBufferSize)
        {
            // If maxResponseBufferSize has no value, there's no backpressure and we can't reasonably timeout draining.
            if (minDataRate == null || maxResponseBufferSize == null)
            {
                return;
            }

            // With full backpressure and a connection adapter there could be 2 two pipes buffering.
            // We already validate that the buffer size is positive.
            // There's no reason to stop timing the write after the connection is closed.
            var oneBufferSize    = maxResponseBufferSize.Value;
            var maxBufferedBytes = oneBufferSize < long.MaxValue / 2 ? oneBufferSize * 2 : long.MaxValue;

            timeoutControl.StartTimingWrite(minDataRate, maxBufferedBytes);
        }
        private async Task TimeFlushAsync(long count, IHttpOutputProducer outputProducer, CancellationToken cancellationToken)
        {
            _timeoutControl.StartTimingWrite(count);

            try
            {
                await _lastFlushTask;
            }
            catch (OperationCanceledException ex)
            {
                outputProducer.Abort(new ConnectionAbortedException(CoreStrings.ConnectionOrStreamAbortedByCancellationToken, ex));
            }
            catch
            {
                // A canceled token is the only reason flush should ever throw.
            }

            _timeoutControl.StopTimingWrite();

            cancellationToken.ThrowIfCancellationRequested();
        }
Exemplo n.º 6
0
        private async Task FlushAsyncAwaited(WritableBufferAwaitable awaitable, long count, CancellationToken cancellationToken)
        {
            // https://github.com/dotnet/corefxlab/issues/1334
            // Since the flush awaitable doesn't currently support multiple awaiters
            // we need to use a task to track the callbacks.
            // All awaiters get the same task
            lock (_flushLock)
            {
                if (_flushTcs == null || _flushTcs.Task.IsCompleted)
                {
                    _flushTcs = new TaskCompletionSource <object>();

                    awaitable.OnCompleted(_flushCompleted);
                }
            }

            _timeoutControl.StartTimingWrite(count);
            await _flushTcs.Task;

            _timeoutControl.StopTimingWrite();

            cancellationToken.ThrowIfCancellationRequested();
        }
Exemplo n.º 7
0
        private async Task WriteDataAsync(int streamId, StreamOutputFlowControl flowControl, ReadOnlySequence <byte> data, long dataLength, bool endStream)
        {
            while (dataLength > 0)
            {
                OutputFlowControlAwaitable availabilityAwaitable;
                var writeTask = Task.CompletedTask;

                lock (_writeLock)
                {
                    if (_completed || flowControl.IsAborted)
                    {
                        break;
                    }

                    var actual = flowControl.AdvanceUpToAndWait(dataLength, out availabilityAwaitable);

                    if (actual > 0)
                    {
                        if (actual < dataLength)
                        {
                            WriteDataUnsynchronized(streamId, data.Slice(0, actual), actual, endStream: false);
                            data        = data.Slice(actual);
                            dataLength -= actual;
                        }
                        else
                        {
                            WriteDataUnsynchronized(streamId, data, actual, endStream);
                            dataLength = 0;
                        }

                        // Don't call TimeFlushUnsynchronizedAsync() since we time this write while also accounting for
                        // flow control induced backpressure below.
                        writeTask = _flusher.FlushAsync();
                    }

                    if (_minResponseDataRate != null)
                    {
                        _timeoutControl.BytesWrittenToBuffer(_minResponseDataRate, _unflushedBytes);
                    }

                    _unflushedBytes = 0;
                }

                // Avoid timing writes that are already complete. This is likely to happen during the last iteration.
                if (availabilityAwaitable == null && writeTask.IsCompleted)
                {
                    continue;
                }

                if (_minResponseDataRate != null)
                {
                    _timeoutControl.StartTimingWrite();
                }

                // This awaitable releases continuations in FIFO order when the window updates.
                // It should be very rare for a continuation to run without any availability.
                if (availabilityAwaitable != null)
                {
                    await availabilityAwaitable;
                }

                await writeTask;

                if (_minResponseDataRate != null)
                {
                    _timeoutControl.StopTimingWrite();
                }
            }

            // Ensure that the application continuation isn't executed inline by ProcessWindowUpdateFrameAsync.
            await ThreadPoolAwaitable.Instance;
        }