use std::future::Future;
use tokio_util::sync::CancellationToken;
#[derive(Debug, Clone, Default)]
pub struct Shutdown {
token: CancellationToken,
}
impl Shutdown {
pub fn new() -> Self {
Self::default()
}
pub fn trigger(&self) {
self.token.cancel();
}
pub fn is_triggered(&self) -> bool {
self.token.is_cancelled()
}
pub async fn wait(&self) {
self.token.cancelled().await;
}
pub fn wait_owned(&self) -> impl Future<Output = ()> + Send + 'static {
self.token.clone().cancelled_owned()
}
pub fn token(&self) -> &CancellationToken {
&self.token
}
}
impl From<CancellationToken> for Shutdown {
fn from(token: CancellationToken) -> Self {
Self { token }
}
}
pub async fn os_signal() {
let ctrl_c = async {
if let Err(err) = tokio::signal::ctrl_c().await {
tracing::warn!(error = %err, "failed to listen for Ctrl+C");
std::future::pending::<()>().await;
}
};
#[cfg(unix)]
let terminate = async {
use tokio::signal::unix::{SignalKind, signal};
match signal(SignalKind::terminate()) {
Ok(mut stream) => {
stream.recv().await;
}
Err(err) => {
tracing::warn!(error = %err, "failed to listen for SIGTERM");
std::future::pending::<()>().await;
}
}
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => tracing::info!("received Ctrl+C, shutting down"),
() = terminate => tracing::info!("received SIGTERM, shutting down"),
}
}