Detatch CancelKeyPress and ProcessExit event handlers when IWebHost.RunAsync is completed. (#6638)

This commit is contained in:
Chris Baudin 2019-01-13 22:44:25 -08:00 committed by David Fowler
parent a5658a8c95
commit 0b8e16f10a
2 changed files with 89 additions and 47 deletions

View File

@ -0,0 +1,70 @@
// 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;
namespace Microsoft.AspNetCore.Hosting.Internal
{
internal class WebHostLifetime : IDisposable
{
private readonly CancellationTokenSource _cts;
private readonly ManualResetEventSlim _resetEvent;
private readonly string _shutdownMessage;
private bool _disposed = false;
public WebHostLifetime(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage)
{
_cts = cts;
_resetEvent = resetEvent;
_shutdownMessage = shutdownMessage;
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
Console.CancelKeyPress += CancelKeyPress;
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
AppDomain.CurrentDomain.ProcessExit -= ProcessExit;
Console.CancelKeyPress -= CancelKeyPress;
}
private void CancelKeyPress(object sender, ConsoleCancelEventArgs eventArgs)
{
Shutdown();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
}
private void ProcessExit(object sender, EventArgs eventArgs)
{
Shutdown();
}
private void Shutdown()
{
if (!_cts.IsCancellationRequested)
{
if (!string.IsNullOrEmpty(_shutdownMessage))
{
Console.WriteLine(_shutdownMessage);
}
try
{
_cts.Cancel();
}
catch (ObjectDisposedException) { }
}
// Wait on the given reset event
_resetEvent.Wait();
}
}
}

View File

@ -43,15 +43,16 @@ namespace Microsoft.AspNetCore.Hosting
var done = new ManualResetEventSlim(false);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token))
{
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: string.Empty);
try
using (var lifetime = new WebHostLifetime(cts, done, shutdownMessage: string.Empty))
{
await host.WaitForTokenShutdownAsync(cts.Token);
}
finally
{
done.Set();
try
{
await host.WaitForTokenShutdownAsync(cts.Token);
}
finally
{
done.Set();
}
}
}
}
@ -84,15 +85,16 @@ namespace Microsoft.AspNetCore.Hosting
using (var cts = new CancellationTokenSource())
{
var shutdownMessage = host.Services.GetRequiredService<WebHostOptions>().SuppressStatusMessages ? string.Empty : "Application is shutting down...";
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: shutdownMessage);
try
using (var lifetime = new WebHostLifetime(cts, done, shutdownMessage: shutdownMessage))
{
await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
}
finally
{
done.Set();
try
{
await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
}
finally
{
done.Set();
}
}
}
}
@ -131,36 +133,6 @@ namespace Microsoft.AspNetCore.Hosting
}
}
private static void AttachCtrlcSigtermShutdown(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage)
{
void Shutdown()
{
if (!cts.IsCancellationRequested)
{
if (!string.IsNullOrEmpty(shutdownMessage))
{
Console.WriteLine(shutdownMessage);
}
try
{
cts.Cancel();
}
catch (ObjectDisposedException) { }
}
// Wait on the given reset event
resetEvent.Wait();
};
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => Shutdown();
Console.CancelKeyPress += (sender, eventArgs) =>
{
Shutdown();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
}
private static async Task WaitForTokenShutdownAsync(this IWebHost host, CancellationToken token)
{
var applicationLifetime = host.Services.GetService<IApplicationLifetime>();
@ -184,4 +156,4 @@ namespace Microsoft.AspNetCore.Hosting
await host.StopAsync();
}
}
}
}