#[cfg(unix)] use tokio::signal::unix::signal; use tokio::sync::broadcast; #[derive(Debug, Clone)] pub enum Interrupted { OsSigInt, UserInt, } #[derive(Debug, Clone)] pub struct Terminator { interrupt_tx: broadcast::Sender, } impl Terminator { pub fn new(interrupt_tx: broadcast::Sender) -> Self { Self { interrupt_tx } } pub fn terminate(&mut self, interrupted: Interrupted) -> anyhow::Result<()> { self.interrupt_tx.send(interrupted)?; Ok(()) } } #[cfg(unix)] async fn terminate_by_unix_signal(mut terminator: Terminator) { let mut interrupt_signal = signal(tokio::signal::unix::SignalKind::interrupt()) .expect("failed to create interrupt signal stream"); let mut terminate_signal = signal(tokio::signal::unix::SignalKind::terminate()) .expect("failed to create terminate signal stream"); tokio::select! { _ = interrupt_signal.recv() => { terminator .terminate(Interrupted::OsSigInt) .expect("failed to send interrupt signal"); } _ = terminate_signal.recv() => { terminator .terminate(Interrupted::OsSigInt) .expect("failed to send terminate signal"); } } } // create a broadcast channel for retrieving the application kill signal pub fn create_termination() -> (Terminator, broadcast::Receiver) { let (tx, rx) = broadcast::channel(1); let terminator = Terminator::new(tx); #[cfg(unix)] tokio::spawn(terminate_by_unix_signal(terminator.clone())); (terminator, rx) }