use std::future::Future;
use tokio::{
signal,
sync::{broadcast, watch},
};
fn register_handlers() -> impl Future<Output = ()> {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install termination signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
async {
tokio::select! {
() = ctrl_c => {},
() = terminate => {},
}
tracing::warn!("received shutdown signal");
}
}
pub type Handle = broadcast::Receiver<()>;
#[derive(Clone)]
pub struct Shutdown {
sender: broadcast::Sender<()>,
}
impl Shutdown {
pub fn new() -> Self {
let (tx, rx) = broadcast::channel(1);
let handle = register_handlers();
tokio::spawn({
let tx = tx.clone();
async move {
handle.await;
tx.send(()).ok();
}
});
Self {
sender: tx,
}
}
pub fn shutdown(&self) {
self.sender.send(()).expect("can send shutdown signal");
}
pub fn handle(&self) -> Handle {
self.sender.subscribe()
}
}