torrust_index/web/api/server/
signals.rs

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