-
Notifications
You must be signed in to change notification settings - Fork 739
/
WindowsServiceLifetime.cs
104 lines (92 loc) · 4.06 KB
/
WindowsServiceLifetime.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// 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.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Hosting.WindowsServices
{
public class WindowsServiceLifetime : ServiceBase, IHostLifetime
{
private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly ManualResetEventSlim _delayStop = new ManualResetEventSlim();
private readonly HostOptions _hostOptions;
public WindowsServiceLifetime(IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor)
{
Environment = environment ?? throw new ArgumentNullException(nameof(environment));
ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
Logger = loggerFactory.CreateLogger("Microsoft.Hosting.Lifetime");
if (optionsAccessor == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
_hostOptions = optionsAccessor.Value;
}
private IHostApplicationLifetime ApplicationLifetime { get; }
private IHostEnvironment Environment { get; }
private ILogger Logger { get; }
public Task WaitForStartAsync(CancellationToken cancellationToken)
{
cancellationToken.Register(() => _delayStart.TrySetCanceled());
ApplicationLifetime.ApplicationStarted.Register(() =>
{
Logger.LogInformation("Application started. Hosting environment: {envName}; Content root path: {contentRoot}",
Environment.EnvironmentName, Environment.ContentRootPath);
});
ApplicationLifetime.ApplicationStopping.Register(() =>
{
Logger.LogInformation("Application is shutting down...");
});
ApplicationLifetime.ApplicationStopped.Register(() =>
{
_delayStop.Set();
});
new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
return _delayStart.Task;
}
private void Run()
{
try
{
Run(this); // This blocks until the service is stopped.
_delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
}
catch (Exception ex)
{
_delayStart.TrySetException(ex);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
// Avoid deadlock where host waits for StopAsync before firing ApplicationStopped,
// and Stop waits for ApplicationStopped.
Task.Run(Stop);
return Task.CompletedTask;
}
// Called by base.Run when the service is ready to start.
protected override void OnStart(string[] args)
{
_delayStart.TrySetResult(null);
base.OnStart(args);
}
// Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
// That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
protected override void OnStop()
{
ApplicationLifetime.StopApplication();
// Wait for the host to shutdown before marking service as stopped.
_delayStop.Wait(_hostOptions.ShutdownTimeout);
base.OnStop();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_delayStop.Set();
}
base.Dispose(disposing);
}
}
}