gracefulShutdown function

Future<void> gracefulShutdown({
  1. int exitCode = 0,
  2. ExitReason reason = ExitReason.other,
  3. String? finalMessage,
})

Graceful shutdown: cleans up resources, runs hooks, flushes analytics, exits.

Implementation

Future<void> gracefulShutdown({
  int exitCode = 0,
  ExitReason reason = ExitReason.other,
  String? finalMessage,
}) async {
  if (_shutdownInProgress) return;
  _shutdownInProgress = true;

  // Resolve the session-end hook budget.
  const sessionEndTimeoutMs = 1500;

  // Failsafe: guarantee process exits even if cleanup hangs.
  final failsafeBudget =
      const Duration(milliseconds: 5000).inMilliseconds >
          sessionEndTimeoutMs + 3500
      ? const Duration(milliseconds: 5000)
      : Duration(milliseconds: sessionEndTimeoutMs + 3500);
  _failsafeTimer = Timer(failsafeBudget, () {
    _cleanupTerminalModes();
    _printResumeHint();
    _forceExit(exitCode);
  });

  // Exit alt screen and print resume hint first.
  _cleanupTerminalModes();
  _printResumeHint();

  // Flush session data — most critical cleanup.
  try {
    await runCleanupFunctions().timeout(const Duration(seconds: 2));
  } catch (_) {
    // Silently handle timeout and other errors.
  }

  // Execute SessionEnd hooks.
  try {
    if (_sessionEndHooks != null) {
      await _sessionEndHooks!(reason, timeoutMs: sessionEndTimeoutMs);
    }
  } catch (_) {
    // Ignore exceptions (including AbortError on timeout).
  }

  // Flush analytics — capped at 500 ms.
  try {
    if (_analyticsShutdown != null) {
      await _analyticsShutdown!().timeout(const Duration(milliseconds: 500));
    }
  } catch (_) {
    // Ignore analytics shutdown errors.
  }

  if (finalMessage != null) {
    try {
      stderr.writeln(finalMessage);
    } catch (_) {
      // stderr may be closed.
    }
  }

  _forceExit(exitCode);
}