torrust_server_lib/
signals.rs1use derive_more::Display;
3use tokio_util::sync::CancellationToken;
4use torrust_net_primitives::service_binding::ServiceBinding;
5use tracing::instrument;
6
7#[derive(Debug)]
11pub struct Started {
12 pub service_binding: ServiceBinding,
13 pub address: std::net::SocketAddr,
14}
15
16#[deprecated(
20 since = "0.5.0",
21 note = "inject a `tokio_util::sync::CancellationToken` instead of sending `Halted` over a oneshot channel; removal is planned for torrust-server-lib 0.6.0"
22)]
23#[derive(Copy, Clone, Debug, Display)]
24pub enum Halted {
25 Normal,
26}
27
28#[instrument(skip(cancellation_token))]
33pub async fn cancellation_signal(cancellation_token: CancellationToken) {
34 cancellation_token.cancelled().await;
35}
36
37#[deprecated(
43 since = "0.5.0",
44 note = "libraries must not subscribe to SIGINT/SIGTERM; the executable should subscribe and cancel a `CancellationToken` passed to `cancellation_signal`; removal is planned for torrust-server-lib 0.6.0"
45)]
46#[instrument(skip())]
47pub async fn global_shutdown_signal() {
48 let ctrl_c = async {
49 tokio::signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
50 };
51
52 #[cfg(unix)]
53 let terminate = async {
54 tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
55 .expect("failed to install signal handler")
56 .recv()
57 .await;
58 };
59
60 #[cfg(not(unix))]
61 let terminate = std::future::pending::<()>();
62
63 tokio::select! {
64 () = ctrl_c => {tracing::warn!("caught interrupt signal (ctrl-c), halting...");},
65 () = terminate => {tracing::warn!("caught interrupt signal (terminate), halting...");}
66 }
67}
68
69#[deprecated(
75 since = "0.5.0",
76 note = "use `cancellation_signal` with an injected `CancellationToken`; removal is planned for torrust-server-lib 0.6.0"
77)]
78#[allow(
79 deprecated,
80 reason = "the legacy function is implemented with the other legacy symbols until they are removed"
81)]
82#[instrument(skip(rx_halt))]
83pub async fn shutdown_signal(rx_halt: tokio::sync::oneshot::Receiver<Halted>) {
84 let halt = async {
85 match rx_halt.await {
86 Ok(signal) => signal,
87 Err(err) => panic!("Failed to install stop signal: {err}"),
88 }
89 };
90
91 tokio::select! {
92 signal = halt => { tracing::debug!("Halt signal processed: {}", signal) },
93 () = global_shutdown_signal() => { tracing::debug!("Global shutdown signal processed") }
94 }
95}
96
97#[deprecated(
99 since = "0.5.0",
100 note = "use `cancellation_signal` with an injected `CancellationToken` and log the message yourself; removal is planned for torrust-server-lib 0.6.0"
101)]
102#[allow(
103 deprecated,
104 reason = "the legacy function is implemented with the other legacy symbols until they are removed"
105)]
106#[instrument(skip(rx_halt))]
107pub async fn shutdown_signal_with_message(rx_halt: tokio::sync::oneshot::Receiver<Halted>, message: String) {
108 shutdown_signal(rx_halt).await;
109
110 tracing::info!("{message}");
111}
112
113#[cfg(test)]
114mod tests {
115 use tokio::sync::oneshot;
116 use tokio_util::sync::CancellationToken;
117
118 #[allow(deprecated, reason = "the test covers the deprecated legacy API until it is removed")]
119 use super::{Halted, cancellation_signal, shutdown_signal};
120
121 #[tokio::test]
122 async fn cancellation_signal_should_resolve_when_the_token_is_cancelled() {
123 let cancellation_token = CancellationToken::new();
124 let wait_task = tokio::spawn(cancellation_signal(cancellation_token.clone()));
125
126 tokio::task::yield_now().await;
127
128 assert!(!wait_task.is_finished());
129
130 cancellation_token.cancel();
131
132 wait_task.await.expect("cancellation wait task should complete");
133 }
134
135 #[allow(deprecated, reason = "the test covers the deprecated legacy API until it is removed")]
136 #[tokio::test]
137 async fn shutdown_signal_should_resolve_when_it_receives_a_legacy_halted_message() {
138 let (halt_sender, halt_receiver) = oneshot::channel();
139 let wait_task = tokio::spawn(shutdown_signal(halt_receiver));
140
141 halt_sender
142 .send(Halted::Normal)
143 .expect("legacy halt receiver should still be waiting");
144
145 wait_task.await.expect("legacy shutdown wait task should complete");
146 }
147}