diff --git a/Cargo.lock b/Cargo.lock index d81dece..2f16799 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,6 +205,17 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -224,6 +235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "slab", @@ -692,6 +704,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", "pin-project-lite", "tokio", ] @@ -709,10 +722,11 @@ dependencies = [ [[package]] name = "torrust-server-lib" -version = "0.2.0" +version = "0.3.0" dependencies = [ "derive_more", "tokio", + "tokio-util", "torrust-net-primitives", "tower-http", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 48610c9..4296fd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,12 @@ publish = true readme = "README.md" repository = "https://github.com/torrust/torrust-server-lib" rust-version = "1.88" -version = "0.2.0" +version = "0.3.0" [dependencies] derive_more = { version = "2", features = [ "as_ref", "constructor", "display", "from" ] } tokio = { version = "1", features = [ "macros", "net", "rt-multi-thread", "signal", "sync" ] } +tokio-util = { version = "0.7", features = [ "rt" ] } torrust-net-primitives = "0.1.0" tower-http = { version = "0", features = [ "compression-full", "cors", "propagate-header", "request-id", "trace" ] } tracing = "0" diff --git a/src/signals.rs b/src/signals.rs index b781a9b..e6b916c 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -1,5 +1,6 @@ //! This module contains functions to handle signals. use derive_more::Display; +use tokio_util::sync::CancellationToken; use torrust_net_primitives::service_binding::ServiceBinding; use tracing::instrument; @@ -20,6 +21,15 @@ pub enum Halted { Normal, } +/// Resolves when the injected cancellation token is cancelled. +/// +/// Unlike [`global_shutdown_signal`], this function never subscribes to an +/// operating-system signal. The caller owns the cancellation lifecycle. +#[instrument(skip(cancellation_token))] +pub async fn cancellation_signal(cancellation_token: CancellationToken) { + cancellation_token.cancelled().await; +} + /// Resolves on `ctrl_c` or the `terminate` signal. /// /// # Panics @@ -75,3 +85,37 @@ pub async fn shutdown_signal_with_message(rx_halt: tokio::sync::oneshot::Receive tracing::info!("{message}"); } + +#[cfg(test)] +mod tests { + use tokio::sync::oneshot; + use tokio_util::sync::CancellationToken; + + use super::{Halted, cancellation_signal, shutdown_signal}; + + #[tokio::test] + async fn cancellation_signal_should_resolve_when_the_token_is_cancelled() { + let cancellation_token = CancellationToken::new(); + let wait_task = tokio::spawn(cancellation_signal(cancellation_token.clone())); + + tokio::task::yield_now().await; + + assert!(!wait_task.is_finished()); + + cancellation_token.cancel(); + + wait_task.await.expect("cancellation wait task should complete"); + } + + #[tokio::test] + async fn shutdown_signal_should_resolve_when_it_receives_a_legacy_halted_message() { + let (halt_sender, halt_receiver) = oneshot::channel(); + let wait_task = tokio::spawn(shutdown_signal(halt_receiver)); + + halt_sender + .send(Halted::Normal) + .expect("legacy halt receiver should still be waiting"); + + wait_task.await.expect("legacy shutdown wait task should complete"); + } +}