stop method
Attempts to gracefully stop the program.
Implementation
// Order:
// HostedLifecycleService.stopping
// HostApplicationLifetime.applicationStopping
// HostedService.stop
// HostedLifecycleService.stopped
// HostApplicationLifetime.applicationStopped
// HostLifetime.stop
Future<void> stop([CancellationToken? cancellationToken]) async {
_stopCalled = true;
_logger.stopping();
cancellationToken ??= CancellationToken.none;
CancellationTokenSource? cts;
if (_options.shutdownTimeout != null) {
cts = CancellationTokenSource.createLinkedTokenSource([cancellationToken])
..cancelAfter(_options.shutdownTimeout!);
cancellationToken = cts.token;
}
if (cts != null) {
var exceptions = <Exception>[];
if (!_hostStarting) {
// Started?
// Call IHostApplicationLifetime.ApplicationStopping.
// This catches all exceptions and does not re-throw.
_applicationLifetime.stopApplication();
} else {
assert(
_hostedServices != null,
'Hosted services are resolved when host is started.',
);
// Ensure hosted services are stopped in LIFO order
var reversedServices =
List<HostedService>.from(_hostedServices ?? <HostedService>[])
.reversed;
Iterable<HostedLifecycleService>? reversedLifetimeServices =
List<HostedLifecycleService>.from(
_hostedLifecycleServices ?? <HostedLifecycleService>[])
.reversed;
var concurrent = _options.servicesStopConcurrently;
// Call stopping.
if (reversedLifetimeServices.isNotEmpty) {
await foreachService<HostedLifecycleService>(
reversedLifetimeServices,
cancellationToken,
concurrent,
false,
exceptions,
(service, token) => service.stopping(token),
);
}
// Call HostApplicationLifetime.applicationStopping.
// This catches all exceptions and does not re-throw.
_applicationLifetime.stopApplication();
if (reversedServices.isNotEmpty) {
await foreachService<HostedService>(
reversedServices,
cancellationToken,
concurrent,
false,
exceptions,
(service, token) => service.stop(token),
);
}
if (reversedLifetimeServices.isNotEmpty) {
await foreachService<HostedLifecycleService>(
reversedLifetimeServices,
cancellationToken,
concurrent,
false,
exceptions,
(service, token) => service.stopped(token),
);
}
}
// Call HostApplicationLifetime.stopped
// This catches all exceptions and does not re-throw.
_applicationLifetime.notifyStopped();
// This may not catch exceptions, so we do it here.
try {
await _hostLifetime.stop(cancellationToken);
} on Exception catch (ex) {
exceptions.add(ex);
}
if (exceptions.isNotEmpty) {
if (exceptions.length == 1) {
// Rethrow if it's a single error
var singleException = exceptions[0];
_logger.stoppedWithException(singleException);
throw singleException;
} else {
var ex = AggregateException(
message: 'One or more hosted services failed to stop.',
innerExceptions: exceptions,
);
_logger.stoppedWithException(ex);
throw ex;
}
}
}
_logger.stopped();
}