stop method

Future<void> stop([
  1. CancellationToken? cancellationToken
])

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();
}