Skip to main content

torrust_server_lib/
signals.rs

1//! This module contains functions to handle signals.
2use derive_more::Display;
3use torrust_net_primitives::service_binding::ServiceBinding;
4use tracing::instrument;
5
6/// This is the message that the "launcher" spawned task sends to the main
7/// application process to notify the service was successfully started.
8///
9#[derive(Debug)]
10pub struct Started {
11    pub service_binding: ServiceBinding,
12    pub address: std::net::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///
18#[derive(Copy, Clone, Debug, Display)]
19pub enum Halted {
20    Normal,
21}
22
23/// Resolves on `ctrl_c` or the `terminate` signal.
24///
25/// # Panics
26///
27/// Will panic if the `ctrl_c` or `terminate` signal resolves with an error.
28#[instrument(skip())]
29pub async fn global_shutdown_signal() {
30    let ctrl_c = async {
31        tokio::signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
32    };
33
34    #[cfg(unix)]
35    let terminate = async {
36        tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
37            .expect("failed to install signal handler")
38            .recv()
39            .await;
40    };
41
42    #[cfg(not(unix))]
43    let terminate = std::future::pending::<()>();
44
45    tokio::select! {
46        () = ctrl_c => {tracing::warn!("caught interrupt signal (ctrl-c), halting...");},
47        () = terminate => {tracing::warn!("caught interrupt signal (terminate), halting...");}
48    }
49}
50
51/// Resolves when the `stop_receiver` or the `global_shutdown_signal()` resolves.
52///
53/// # Panics
54///
55/// Will panic if the `stop_receiver` resolves with an error.
56#[instrument(skip(rx_halt))]
57pub async fn shutdown_signal(rx_halt: tokio::sync::oneshot::Receiver<Halted>) {
58    let halt = async {
59        match rx_halt.await {
60            Ok(signal) => signal,
61            Err(err) => panic!("Failed to install stop signal: {err}"),
62        }
63    };
64
65    tokio::select! {
66        signal = halt => { tracing::debug!("Halt signal processed: {}", signal) },
67        () = global_shutdown_signal() => { tracing::debug!("Global shutdown signal processed") }
68    }
69}
70
71/// Same as `shutdown_signal()`, but shows a message when it resolves.
72#[instrument(skip(rx_halt))]
73pub async fn shutdown_signal_with_message(rx_halt: tokio::sync::oneshot::Receiver<Halted>, message: String) {
74    shutdown_signal(rx_halt).await;
75
76    tracing::info!("{message}");
77}