torrust_tracker/servers/
signals.rs

1//! This module contains functions to handle signals.
2use std::time::Duration;
3
4use derive_more::Display;
5use tokio::time::sleep;
6use tracing::instrument;
7
8/// This is the message that the "launcher" spawned task receives from the main
9/// application process to notify the service to shutdown.
10///
11#[derive(Copy, Clone, Debug, Display)]
12pub enum Halted {
13    Normal,
14}
15
16/// Resolves on `ctrl_c` or the `terminate` signal.
17///
18/// # Panics
19///
20/// Will panic if the `ctrl_c` or `terminate` signal resolves with an error.
21#[instrument(skip())]
22pub async fn global_shutdown_signal() {
23    let ctrl_c = async {
24        tokio::signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
25    };
26
27    #[cfg(unix)]
28    let terminate = async {
29        tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
30            .expect("failed to install signal handler")
31            .recv()
32            .await;
33    };
34
35    #[cfg(not(unix))]
36    let terminate = std::future::pending::<()>();
37
38    tokio::select! {
39        () = ctrl_c => {tracing::warn!("caught interrupt signal (ctrl-c), halting...");},
40        () = terminate => {tracing::warn!("caught interrupt signal (terminate), halting...");}
41    }
42}
43
44/// Resolves when the `stop_receiver` or the `global_shutdown_signal()` resolves.
45///
46/// # Panics
47///
48/// Will panic if the `stop_receiver` resolves with an error.
49#[instrument(skip(rx_halt))]
50pub async fn shutdown_signal(rx_halt: tokio::sync::oneshot::Receiver<Halted>) {
51    let halt = async {
52        match rx_halt.await {
53            Ok(signal) => signal,
54            Err(err) => panic!("Failed to install stop signal: {err}"),
55        }
56    };
57
58    tokio::select! {
59        signal = halt => { tracing::debug!("Halt signal processed: {}", signal) },
60        () = global_shutdown_signal() => { tracing::debug!("Global shutdown signal processed") }
61    }
62}
63
64/// Same as `shutdown_signal()`, but shows a message when it resolves.
65#[instrument(skip(rx_halt))]
66pub async fn shutdown_signal_with_message(rx_halt: tokio::sync::oneshot::Receiver<Halted>, message: String) {
67    shutdown_signal(rx_halt).await;
68
69    tracing::info!("{message}");
70}
71
72#[instrument(skip(handle, rx_halt, message))]
73pub async fn graceful_shutdown(handle: axum_server::Handle, rx_halt: tokio::sync::oneshot::Receiver<Halted>, message: String) {
74    shutdown_signal_with_message(rx_halt, message).await;
75
76    tracing::debug!("Sending graceful shutdown signal");
77    handle.graceful_shutdown(Some(Duration::from_secs(90)));
78
79    println!("!! shuting down in 90 seconds !!");
80
81    loop {
82        sleep(Duration::from_secs(1)).await;
83
84        tracing::info!("remaining alive connections: {}", handle.connection_count());
85    }
86}