Skip to main content

soma_http_server/
shutdown.rs

1//! Graceful shutdown signal.
2//!
3//! Resolves on `Ctrl+C` or, on Unix, `SIGTERM` — the two signals a process
4//! manager (systemd, Docker, Kubernetes) or an interactive terminal sends to
5//! ask a server to stop accepting new work and drain in-flight requests.
6//!
7//! Known limitation: if registering a signal handler itself fails (e.g. a
8//! sandboxed/seccomp-restricted environment that disallows the underlying
9//! syscalls), that branch logs an `error` and then never resolves — it does
10//! not abort the process, it just stops competing in the `tokio::select!`
11//! below so the *other* signal can still trigger shutdown. If both
12//! registrations fail, this future never resolves at all and the only way
13//! to stop the process becomes an uncatchable `SIGKILL` (no request
14//! draining). That failure mode is easy to miss: the only evidence is a
15//! startup-time log line, well before the eventual shutdown attempt that
16//! silently doesn't work. Since SIGTERM is the primary signal process
17//! managers actually send, a failed SIGTERM registration in particular
18//! means production `docker stop`/`kubectl delete pod` shutdowns hard-kill
19//! instead of draining, not just Ctrl+C in a local terminal.
20pub async fn shutdown_signal() {
21    let ctrl_c = async {
22        if let Err(error) = tokio::signal::ctrl_c().await {
23            tracing::error!(%error, "CTRL+C handler failed");
24            std::future::pending::<()>().await;
25        }
26    };
27
28    #[cfg(unix)]
29    let terminate = async {
30        match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
31            Ok(mut signal) => {
32                signal.recv().await;
33            }
34            Err(error) => {
35                tracing::error!(%error, "SIGTERM handler failed");
36                std::future::pending::<()>().await;
37            }
38        }
39    };
40
41    #[cfg(not(unix))]
42    let terminate = std::future::pending::<()>();
43
44    tokio::select! {
45        () = ctrl_c => {}
46        () = terminate => {}
47    }
48    tracing::info!("shutdown signal received");
49}