use tokio::sync::watch;
#[derive(Clone, Debug)]
pub struct ShutdownHandle {
sender: watch::Sender<bool>,
receiver: watch::Receiver<bool>,
}
impl ShutdownHandle {
pub fn new() -> Self {
let (sender, receiver) = watch::channel(false);
Self { sender, receiver }
}
pub fn shutdown(&self) {
let _ = self.sender.send(true);
}
pub fn is_shutting_down(&self) -> bool {
*self.receiver.borrow()
}
pub fn token(&self) -> ShutdownToken {
ShutdownToken {
receiver: self.receiver.clone(),
}
}
pub fn register_signals(&self) -> crate::Result<()> {
let runtime = tokio::runtime::Handle::try_current().map_err(crate::Error::NoRuntime)?;
#[cfg(unix)]
let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.map_err(crate::Error::ShutdownInit)?;
let handle = self.clone();
tracing::debug!("registering shutdown signal handlers");
runtime.spawn(async move {
let ctrl_c = tokio::signal::ctrl_c();
#[cfg(unix)]
tokio::select! {
_ = ctrl_c => {},
_ = sigterm.recv() => {},
}
#[cfg(not(unix))]
ctrl_c.await.ok();
tracing::info!("shutdown signal received");
handle.shutdown();
});
Ok(())
}
}
impl Default for ShutdownHandle {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct ShutdownToken {
receiver: watch::Receiver<bool>,
}
impl ShutdownToken {
pub async fn cancelled(&mut self) {
loop {
if *self.receiver.borrow_and_update() {
return;
}
if self.receiver.changed().await.is_err() {
tracing::warn!("shutdown handle dropped without triggering shutdown");
std::future::pending::<()>().await;
}
}
}
pub fn is_shutting_down(&self) -> bool {
*self.receiver.borrow()
}
}