Surface fatal exceptions that stop the event loop
- Request an app Shutdown so KestrelEngine gets disposed - Ensure Listener.Dispose doesn't deadlock before the engine can be disposed - Rely on the existing logic to rethrow in the fatal error when the engine gets disposed
This commit is contained in:
parent
c1ea96d1e0
commit
b9901c3bfe
|
|
@ -18,10 +18,12 @@ namespace Kestrel
|
|||
public class ServerFactory : IServerFactory
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IApplicationShutdown _appShutdownService;
|
||||
|
||||
public ServerFactory(ILibraryManager libraryManager)
|
||||
public ServerFactory(ILibraryManager libraryManager, IApplicationShutdown appShutdownService)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_appShutdownService = appShutdownService;
|
||||
}
|
||||
|
||||
public IServerInformation Initialize(IConfiguration configuration)
|
||||
|
|
@ -35,7 +37,7 @@ namespace Kestrel
|
|||
{
|
||||
var disposables = new List<IDisposable>();
|
||||
var information = (ServerInformation)serverInformation;
|
||||
var engine = new KestrelEngine(_libraryManager);
|
||||
var engine = new KestrelEngine(_libraryManager, _appShutdownService);
|
||||
engine.Start(1);
|
||||
foreach (var address in information.Addresses)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,22 +76,32 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
Thread.Post(
|
||||
_ =>
|
||||
{
|
||||
try
|
||||
// Ensure the event loop is still running.
|
||||
// If the event loop isn't running and we try to wait on this Post
|
||||
// to complete, then KestrelEngine will never be disposed and
|
||||
// the exception that stopped the event loop will never be surfaced.
|
||||
if (Thread.FatalError == null)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
Thread.Post(
|
||||
_ =>
|
||||
{
|
||||
ListenSocket.Dispose();
|
||||
tcs.SetResult(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.SetException(ex);
|
||||
}
|
||||
},
|
||||
null);
|
||||
tcs.Task.Wait();
|
||||
try
|
||||
{
|
||||
ListenSocket.Dispose();
|
||||
tcs.SetResult(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.SetException(ex);
|
||||
}
|
||||
},
|
||||
null);
|
||||
|
||||
// REVIEW: Should we add a timeout here to be safe?
|
||||
tcs.Task.Wait();
|
||||
}
|
||||
|
||||
ListenSocket = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
}
|
||||
|
||||
public UvLoopHandle Loop { get { return _loop; } }
|
||||
public ExceptionDispatchInfo FatalError { get { return _closeError; } }
|
||||
|
||||
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; internal set; }
|
||||
|
||||
|
|
@ -173,6 +174,9 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
catch (Exception ex)
|
||||
{
|
||||
_closeError = ExceptionDispatchInfo.Capture(ex);
|
||||
// Request shutdown so we can rethrow this exception
|
||||
// in Stop which should be observable.
|
||||
_engine.AppShutdown.RequestShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
{
|
||||
public class KestrelEngine : IDisposable
|
||||
{
|
||||
|
||||
public KestrelEngine(ILibraryManager libraryManager)
|
||||
public KestrelEngine(ILibraryManager libraryManager, IApplicationShutdown appShutdownService)
|
||||
{
|
||||
AppShutdown = appShutdownService;
|
||||
Threads = new List<KestrelThread>();
|
||||
Listeners = new List<Listener>();
|
||||
Memory = new MemoryPool();
|
||||
|
|
@ -63,6 +63,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
|
||||
public Libuv Libuv { get; private set; }
|
||||
public IMemoryPool Memory { get; set; }
|
||||
public IApplicationShutdown AppShutdown { get; private set; }
|
||||
public List<KestrelThread> Threads { get; private set; }
|
||||
public List<Listener> Listeners { get; private set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
[Fact]
|
||||
public async Task EngineCanStartAndStop()
|
||||
{
|
||||
var engine = new KestrelEngine(LibraryManager);
|
||||
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||
engine.Start(1);
|
||||
engine.Dispose();
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
[Fact]
|
||||
public async Task ListenerCanCreateAndDispose()
|
||||
{
|
||||
var engine = new KestrelEngine(LibraryManager);
|
||||
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||
engine.Start(1);
|
||||
var started = engine.CreateServer("http", "localhost", 54321, App);
|
||||
started.Dispose();
|
||||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
[Fact]
|
||||
public async Task ConnectionCanReadAndWrite()
|
||||
{
|
||||
var engine = new KestrelEngine(LibraryManager);
|
||||
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||
engine.Start(1);
|
||||
var started = engine.CreateServer("http", "localhost", 54321, App);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
Libuv _uv;
|
||||
public NetworkingTests()
|
||||
{
|
||||
var engine = new KestrelEngine(LibraryManager);
|
||||
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||
_uv = engine.Libuv;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
// 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.Threading;
|
||||
using Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.Server.KestrelTests
|
||||
{
|
||||
public class ShutdownNotImplemented : IApplicationShutdown
|
||||
{
|
||||
public CancellationToken ShutdownRequested
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestShutdown()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
|
||||
public void Create(Func<Frame, Task> app)
|
||||
{
|
||||
_engine = new KestrelEngine(LibraryManager);
|
||||
_engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||
_engine.Start(1);
|
||||
_server = _engine.CreateServer(
|
||||
"http",
|
||||
|
|
|
|||
Loading…
Reference in New Issue