Don't complete KestrelServer.StopAsync task inline (#2534)

This commit is contained in:
Stephen Halter 2018-05-03 17:01:36 -07:00 committed by GitHub
parent 45ba132e34
commit 8b1fbad10e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 1 deletions

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
private bool _hasStarted;
private int _stopping;
private readonly TaskCompletionSource<object> _stoppedTcs = new TaskCompletionSource<object>();
private readonly TaskCompletionSource<object> _stoppedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
public KestrelServer(IOptions<KestrelServerOptions> options, ITransportFactory transportFactory, ILoggerFactory loggerFactory)
: this(transportFactory, CreateServiceContext(options, loggerFactory))

View File

@ -327,6 +327,63 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
mockTransport.Verify(transport => transport.UnbindAsync(), Times.Once);
}
[Fact]
public async Task StopAsyncDispatchesSubsequentStopAsyncContinuations()
{
var options = new KestrelServerOptions
{
ListenOptions =
{
new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
}
};
var unbindTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockTransport = new Mock<ITransport>();
mockTransport
.Setup(transport => transport.BindAsync())
.Returns(Task.CompletedTask);
mockTransport
.Setup(transport => transport.UnbindAsync())
.Returns(unbindTcs.Task);
mockTransport
.Setup(transport => transport.StopAsync())
.Returns(Task.CompletedTask);
var mockTransportFactory = new Mock<ITransportFactory>();
mockTransportFactory
.Setup(transportFactory => transportFactory.Create(It.IsAny<IEndPointInformation>(), It.IsAny<IConnectionDispatcher>()))
.Returns(mockTransport.Object);
var mockLoggerFactory = new Mock<ILoggerFactory>();
var mockLogger = new Mock<ILogger>();
mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny<string>())).Returns(mockLogger.Object);
var server = new KestrelServer(Options.Create(options), mockTransportFactory.Object, mockLoggerFactory.Object);
await server.StartAsync(new DummyApplication(), default);
var stopTask1 = server.StopAsync(default);
var stopTask2 = server.StopAsync(default);
Assert.False(stopTask1.IsCompleted);
Assert.False(stopTask2.IsCompleted);
var continuationTask = Task.Run(async () =>
{
await stopTask2;
stopTask1.Wait();
});
unbindTcs.SetResult(null);
// If stopTask2 is completed inline by the first call to StopAsync, stopTask1 will never complete.
await stopTask1.TimeoutAfter(TestConstants.DefaultTimeout);
await stopTask2.TimeoutAfter(TestConstants.DefaultTimeout);
await continuationTask.TimeoutAfter(TestConstants.DefaultTimeout);
mockTransport.Verify(transport => transport.UnbindAsync(), Times.Once);
}
private static KestrelServer CreateServer(KestrelServerOptions options, ILogger testLogger)
{
return new KestrelServer(Options.Create(options), new MockTransportFactory(), new LoggerFactory(new[] { new KestrelTestLoggerProvider(testLogger) }));