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
|
public class ServerFactory : IServerFactory
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IApplicationShutdown _appShutdownService;
|
||||||
|
|
||||||
public ServerFactory(ILibraryManager libraryManager)
|
public ServerFactory(ILibraryManager libraryManager, IApplicationShutdown appShutdownService)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_appShutdownService = appShutdownService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServerInformation Initialize(IConfiguration configuration)
|
public IServerInformation Initialize(IConfiguration configuration)
|
||||||
|
|
@ -35,7 +37,7 @@ namespace Kestrel
|
||||||
{
|
{
|
||||||
var disposables = new List<IDisposable>();
|
var disposables = new List<IDisposable>();
|
||||||
var information = (ServerInformation)serverInformation;
|
var information = (ServerInformation)serverInformation;
|
||||||
var engine = new KestrelEngine(_libraryManager);
|
var engine = new KestrelEngine(_libraryManager, _appShutdownService);
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
foreach (var address in information.Addresses)
|
foreach (var address in information.Addresses)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -76,22 +76,32 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<int>();
|
// Ensure the event loop is still running.
|
||||||
Thread.Post(
|
// 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.
|
||||||
try
|
if (Thread.FatalError == null)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
Thread.Post(
|
||||||
|
_ =>
|
||||||
{
|
{
|
||||||
ListenSocket.Dispose();
|
try
|
||||||
tcs.SetResult(0);
|
{
|
||||||
}
|
ListenSocket.Dispose();
|
||||||
catch (Exception ex)
|
tcs.SetResult(0);
|
||||||
{
|
}
|
||||||
tcs.SetException(ex);
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
},
|
tcs.SetException(ex);
|
||||||
null);
|
}
|
||||||
tcs.Task.Wait();
|
},
|
||||||
|
null);
|
||||||
|
|
||||||
|
// REVIEW: Should we add a timeout here to be safe?
|
||||||
|
tcs.Task.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
ListenSocket = null;
|
ListenSocket = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
}
|
}
|
||||||
|
|
||||||
public UvLoopHandle Loop { get { return _loop; } }
|
public UvLoopHandle Loop { get { return _loop; } }
|
||||||
|
public ExceptionDispatchInfo FatalError { get { return _closeError; } }
|
||||||
|
|
||||||
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; internal set; }
|
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; internal set; }
|
||||||
|
|
||||||
|
|
@ -173,6 +174,9 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_closeError = ExceptionDispatchInfo.Capture(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 class KestrelEngine : IDisposable
|
||||||
{
|
{
|
||||||
|
public KestrelEngine(ILibraryManager libraryManager, IApplicationShutdown appShutdownService)
|
||||||
public KestrelEngine(ILibraryManager libraryManager)
|
|
||||||
{
|
{
|
||||||
|
AppShutdown = appShutdownService;
|
||||||
Threads = new List<KestrelThread>();
|
Threads = new List<KestrelThread>();
|
||||||
Listeners = new List<Listener>();
|
Listeners = new List<Listener>();
|
||||||
Memory = new MemoryPool();
|
Memory = new MemoryPool();
|
||||||
|
|
@ -63,6 +63,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
|
|
||||||
public Libuv Libuv { get; private set; }
|
public Libuv Libuv { get; private set; }
|
||||||
public IMemoryPool Memory { get; set; }
|
public IMemoryPool Memory { get; set; }
|
||||||
|
public IApplicationShutdown AppShutdown { get; private set; }
|
||||||
public List<KestrelThread> Threads { get; private set; }
|
public List<KestrelThread> Threads { get; private set; }
|
||||||
public List<Listener> Listeners { get; private set; }
|
public List<Listener> Listeners { get; private set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task EngineCanStartAndStop()
|
public async Task EngineCanStartAndStop()
|
||||||
{
|
{
|
||||||
var engine = new KestrelEngine(LibraryManager);
|
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
engine.Dispose();
|
engine.Dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ListenerCanCreateAndDispose()
|
public async Task ListenerCanCreateAndDispose()
|
||||||
{
|
{
|
||||||
var engine = new KestrelEngine(LibraryManager);
|
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
var started = engine.CreateServer("http", "localhost", 54321, App);
|
var started = engine.CreateServer("http", "localhost", 54321, App);
|
||||||
started.Dispose();
|
started.Dispose();
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ConnectionCanReadAndWrite()
|
public async Task ConnectionCanReadAndWrite()
|
||||||
{
|
{
|
||||||
var engine = new KestrelEngine(LibraryManager);
|
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
var started = engine.CreateServer("http", "localhost", 54321, App);
|
var started = engine.CreateServer("http", "localhost", 54321, App);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
Libuv _uv;
|
Libuv _uv;
|
||||||
public NetworkingTests()
|
public NetworkingTests()
|
||||||
{
|
{
|
||||||
var engine = new KestrelEngine(LibraryManager);
|
var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||||
_uv = engine.Libuv;
|
_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)
|
public void Create(Func<Frame, Task> app)
|
||||||
{
|
{
|
||||||
_engine = new KestrelEngine(LibraryManager);
|
_engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented());
|
||||||
_engine.Start(1);
|
_engine.Start(1);
|
||||||
_server = _engine.CreateServer(
|
_server = _engine.CreateServer(
|
||||||
"http",
|
"http",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue