/
Host.cs
197 lines (170 loc) · 7.78 KB
/
Host.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Hosting.Internal
{
internal sealed class Host : IHost, IAsyncDisposable
{
private readonly ILogger<Host> _logger;
private readonly IHostLifetime _hostLifetime;
private readonly ApplicationLifetime _applicationLifetime;
private readonly HostOptions _options;
private readonly IHostEnvironment _hostEnvironment;
private readonly PhysicalFileProvider _defaultProvider;
private IEnumerable<IHostedService> _hostedServices;
private volatile bool _stopCalled;
public Host(IServiceProvider services,
IHostEnvironment hostEnvironment,
PhysicalFileProvider defaultProvider,
IHostApplicationLifetime applicationLifetime,
ILogger<Host> logger,
IHostLifetime hostLifetime,
IOptions<HostOptions> options)
{
Services = services ?? throw new ArgumentNullException(nameof(services));
_applicationLifetime = (applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime))) as ApplicationLifetime;
_hostEnvironment = hostEnvironment;
_defaultProvider = defaultProvider;
if (_applicationLifetime is null)
{
throw new ArgumentException("Replacing IHostApplicationLifetime is not supported.", nameof(applicationLifetime));
}
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_hostLifetime = hostLifetime ?? throw new ArgumentNullException(nameof(hostLifetime));
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
}
public IServiceProvider Services { get; }
public async Task StartAsync(CancellationToken cancellationToken = default)
{
_logger.Starting();
using var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _applicationLifetime.ApplicationStopping);
CancellationToken combinedCancellationToken = combinedCancellationTokenSource.Token;
await _hostLifetime.WaitForStartAsync(combinedCancellationToken).ConfigureAwait(false);
combinedCancellationToken.ThrowIfCancellationRequested();
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();
foreach (IHostedService hostedService in _hostedServices)
{
// Fire IHostedService.Start
await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
if (hostedService is BackgroundService backgroundService)
{
_ = TryExecuteBackgroundServiceAsync(backgroundService);
}
}
// Fire IHostApplicationLifetime.Started
_applicationLifetime.NotifyStarted();
_logger.Started();
}
private async Task TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)
{
// backgroundService.ExecuteTask may not be set (e.g. if the derived class doesn't call base.StartAsync)
Task backgroundTask = backgroundService.ExecuteTask;
if (backgroundTask == null)
{
return;
}
try
{
await backgroundTask.ConfigureAwait(false);
}
catch (Exception ex)
{
// When the host is being stopped, it cancels the background services.
// This isn't an error condition, so don't log it as an error.
if (_stopCalled && backgroundTask.IsCanceled && ex is OperationCanceledException)
{
return;
}
_logger.BackgroundServiceFaulted(ex);
if (_options.BackgroundServiceExceptionBehavior == BackgroundServiceExceptionBehavior.StopHost)
{
_logger.BackgroundServiceStoppingHost(ex);
_applicationLifetime.StopApplication();
}
}
}
public async Task StopAsync(CancellationToken cancellationToken = default)
{
_stopCalled = true;
_logger.Stopping();
using (var cts = new CancellationTokenSource(_options.ShutdownTimeout))
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken))
{
CancellationToken token = linkedCts.Token;
// Trigger IHostApplicationLifetime.ApplicationStopping
_applicationLifetime.StopApplication();
IList<Exception> exceptions = new List<Exception>();
if (_hostedServices != null) // Started?
{
foreach (IHostedService hostedService in _hostedServices.Reverse())
{
try
{
await hostedService.StopAsync(token).ConfigureAwait(false);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
}
// Fire IHostApplicationLifetime.Stopped
_applicationLifetime.NotifyStopped();
try
{
await _hostLifetime.StopAsync(token).ConfigureAwait(false);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
if (exceptions.Count > 0)
{
var ex = new AggregateException("One or more hosted services failed to stop.", exceptions);
_logger.StoppedWithException(ex);
throw ex;
}
}
_logger.Stopped();
}
public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();
public async ValueTask DisposeAsync()
{
// The user didn't change the ContentRootFileProvider instance, we can dispose it
if (ReferenceEquals(_hostEnvironment.ContentRootFileProvider, _defaultProvider))
{
// Dispose the content provider
await DisposeAsync(_hostEnvironment.ContentRootFileProvider).ConfigureAwait(false);
}
else
{
// In the rare case that the user replaced the ContentRootFileProvider, dispose it and the one
// we originally created
await DisposeAsync(_hostEnvironment.ContentRootFileProvider).ConfigureAwait(false);
await DisposeAsync(_defaultProvider).ConfigureAwait(false);
}
// Dispose the service provider
await DisposeAsync(Services).ConfigureAwait(false);
static async ValueTask DisposeAsync(object o)
{
switch (o)
{
case IAsyncDisposable asyncDisposable:
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
break;
case IDisposable disposable:
disposable.Dispose();
break;
}
}
}
}
}