pub async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("Failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("Failed to install SIGTERM handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
pub struct ShutdownController {
tx: tokio::sync::watch::Sender<bool>,
rx: tokio::sync::watch::Receiver<bool>,
}
impl ShutdownController {
pub fn new() -> Self {
let (tx, rx) = tokio::sync::watch::channel(false);
Self { tx, rx }
}
pub fn subscribe(&self) -> tokio::sync::watch::Receiver<bool> {
self.rx.clone()
}
pub fn shutdown(&self) {
let _ = self.tx.send(true);
}
pub async fn wait(&mut self) {
while !*self.rx.borrow() {
if self.rx.changed().await.is_err() {
break;
}
}
}
}
impl Default for ShutdownController {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_shutdown_controller() {
let controller = ShutdownController::new();
let mut rx = controller.subscribe();
assert!(!*rx.borrow());
controller.shutdown();
rx.changed().await.unwrap();
assert!(*rx.borrow());
}
}