Skip to content

Commit 0b8e16f

Browse files
CBauddavidfowl
authored andcommitted
Detatch CancelKeyPress and ProcessExit event handlers when IWebHost.RunAsync is completed. (#6638)
1 parent a5658a8 commit 0b8e16f

File tree

2 files changed

+89
-47
lines changed

2 files changed

+89
-47
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading;
6+
7+
namespace Microsoft.AspNetCore.Hosting.Internal
8+
{
9+
internal class WebHostLifetime : IDisposable
10+
{
11+
private readonly CancellationTokenSource _cts;
12+
private readonly ManualResetEventSlim _resetEvent;
13+
private readonly string _shutdownMessage;
14+
15+
private bool _disposed = false;
16+
17+
public WebHostLifetime(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage)
18+
{
19+
_cts = cts;
20+
_resetEvent = resetEvent;
21+
_shutdownMessage = shutdownMessage;
22+
23+
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
24+
Console.CancelKeyPress += CancelKeyPress;
25+
}
26+
27+
public void Dispose()
28+
{
29+
if (_disposed)
30+
{
31+
return;
32+
}
33+
34+
_disposed = true;
35+
AppDomain.CurrentDomain.ProcessExit -= ProcessExit;
36+
Console.CancelKeyPress -= CancelKeyPress;
37+
}
38+
39+
private void CancelKeyPress(object sender, ConsoleCancelEventArgs eventArgs)
40+
{
41+
Shutdown();
42+
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
43+
eventArgs.Cancel = true;
44+
}
45+
46+
private void ProcessExit(object sender, EventArgs eventArgs)
47+
{
48+
Shutdown();
49+
}
50+
51+
private void Shutdown()
52+
{
53+
if (!_cts.IsCancellationRequested)
54+
{
55+
if (!string.IsNullOrEmpty(_shutdownMessage))
56+
{
57+
Console.WriteLine(_shutdownMessage);
58+
}
59+
try
60+
{
61+
_cts.Cancel();
62+
}
63+
catch (ObjectDisposedException) { }
64+
}
65+
66+
// Wait on the given reset event
67+
_resetEvent.Wait();
68+
}
69+
}
70+
}

src/Hosting/Hosting/src/WebHostExtensions.cs

Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@ public static async Task WaitForShutdownAsync(this IWebHost host, CancellationTo
4343
var done = new ManualResetEventSlim(false);
4444
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token))
4545
{
46-
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: string.Empty);
47-
48-
try
49-
{
50-
await host.WaitForTokenShutdownAsync(cts.Token);
51-
}
52-
finally
46+
using (var lifetime = new WebHostLifetime(cts, done, shutdownMessage: string.Empty))
5347
{
54-
done.Set();
48+
try
49+
{
50+
await host.WaitForTokenShutdownAsync(cts.Token);
51+
}
52+
finally
53+
{
54+
done.Set();
55+
}
5556
}
5657
}
5758
}
@@ -84,15 +85,16 @@ public static async Task RunAsync(this IWebHost host, CancellationToken token =
8485
using (var cts = new CancellationTokenSource())
8586
{
8687
var shutdownMessage = host.Services.GetRequiredService<WebHostOptions>().SuppressStatusMessages ? string.Empty : "Application is shutting down...";
87-
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: shutdownMessage);
88-
89-
try
90-
{
91-
await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
92-
}
93-
finally
88+
using (var lifetime = new WebHostLifetime(cts, done, shutdownMessage: shutdownMessage))
9489
{
95-
done.Set();
90+
try
91+
{
92+
await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
93+
}
94+
finally
95+
{
96+
done.Set();
97+
}
9698
}
9799
}
98100
}
@@ -131,36 +133,6 @@ private static async Task RunAsync(this IWebHost host, CancellationToken token,
131133
}
132134
}
133135

134-
private static void AttachCtrlcSigtermShutdown(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage)
135-
{
136-
void Shutdown()
137-
{
138-
if (!cts.IsCancellationRequested)
139-
{
140-
if (!string.IsNullOrEmpty(shutdownMessage))
141-
{
142-
Console.WriteLine(shutdownMessage);
143-
}
144-
try
145-
{
146-
cts.Cancel();
147-
}
148-
catch (ObjectDisposedException) { }
149-
}
150-
151-
// Wait on the given reset event
152-
resetEvent.Wait();
153-
};
154-
155-
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => Shutdown();
156-
Console.CancelKeyPress += (sender, eventArgs) =>
157-
{
158-
Shutdown();
159-
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
160-
eventArgs.Cancel = true;
161-
};
162-
}
163-
164136
private static async Task WaitForTokenShutdownAsync(this IWebHost host, CancellationToken token)
165137
{
166138
var applicationLifetime = host.Services.GetService<IApplicationLifetime>();
@@ -184,4 +156,4 @@ private static async Task WaitForTokenShutdownAsync(this IWebHost host, Cancella
184156
await host.StopAsync();
185157
}
186158
}
187-
}
159+
}

0 commit comments

Comments
 (0)