Merge pull request #2736 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Chris Ross 2018-07-19 13:25:01 -07:00 committed by GitHub
commit 475a5a9a51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 20 deletions

View File

@ -59,6 +59,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
Log.LogCritical(0, ex, $"{nameof(ConnectionDispatcher)}.{nameof(Execute)}() {connectionContext.ConnectionId}");
}
finally
{
// Complete the transport PipeReader and PipeWriter after calling into application code
connectionContext.Transport.Input.Complete();
connectionContext.Transport.Output.Complete();
}
}
}

View File

@ -1,7 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
@ -42,5 +44,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Verify the scope was disposed after request processing completed
Assert.True(((TestKestrelTrace)serviceContext.Log).Logger.Scopes.IsEmpty);
}
[Fact]
public async Task OnConnectionCompletesTransportPipesAfterReturning()
{
var serviceContext = new TestServiceContext();
var tcs = new TaskCompletionSource<object>();
var dispatcher = new ConnectionDispatcher(serviceContext, _ => Task.CompletedTask);
var mockConnection = new Mock<TransportConnection>();
var mockPipeReader = new Mock<PipeReader>();
var mockPipeWriter = new Mock<PipeWriter>();
var mockPipe = new Mock<IDuplexPipe>();
mockPipe.Setup(m => m.Input).Returns(mockPipeReader.Object);
mockPipe.Setup(m => m.Output).Returns(mockPipeWriter.Object);
mockConnection.Setup(m => m.Transport).Returns(mockPipe.Object);
var connection = mockConnection.Object;
await dispatcher.OnConnection(connection);
mockPipeWriter.Verify(m => m.Complete(It.IsAny<Exception>()), Times.Once());
mockPipeReader.Verify(m => m.Complete(It.IsAny<Exception>()), Times.Once());
}
}
}

View File

@ -131,14 +131,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public Http2ConnectionTests()
{
var inlineSchedulingPipeOptions = new PipeOptions(
// Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
// Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
// it easier to verify request frames are processed correctly immediately after sending the them.
var inputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.Inline,
writerScheduler: PipeScheduler.ThreadPool,
useSynchronizationContext: false
);
var outputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.ThreadPool,
writerScheduler: PipeScheduler.Inline,
useSynchronizationContext: false
);
_pair = DuplexPipe.CreateConnectionPair(inlineSchedulingPipeOptions, inlineSchedulingPipeOptions);
_pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
_noopApplication = context => Task.CompletedTask;
@ -488,12 +497,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 3);
// Ensure that Http2FrameWriter._writeLock isn't acquired when completing the frame processing loop.
// Otherwise there's a deadlock where Http2OutputProducer.Abort() being called from the frame processing
// loop blocks waiting Http2OutputProducer.Dispose() being called from the stream processing loop to
// acquire the _writeLock.
await ThreadPoolAwaitable.Instance;
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
Assert.Equal(stream1DataFrame1.DataPayload, _helloBytes);
@ -565,12 +568,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
// Ensure that Http2FrameWriter._writeLock isn't acquired when completing the frame processing loop.
// Otherwise there's a deadlock where Http2OutputProducer.Abort() being called from the frame processing
// loop blocks waiting Http2OutputProducer.Dispose() being called from the stream processing loop to
// acquire the _writeLock.
await ThreadPoolAwaitable.Instance;
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
}
@ -868,12 +865,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
// Ensure that Http2FrameWriter._writeLock isn't acquired when completing the frame processing loop.
// Otherwise there's a deadlock where Http2OutputProducer.Abort() being called from the frame processing
// loop blocks waiting Http2OutputProducer.Dispose() being called from the stream processing loop to
// acquire the _writeLock.
await ThreadPoolAwaitable.Instance;
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
await WaitForAllStreamsAsync();
}