use std::sync::OnceLock;
use std::time::Duration;
use tokio_util::sync::CancellationToken;
use tracing::{debug, info};
static GLOBAL_SHUTDOWN: OnceLock<CancellationToken> = OnceLock::new();
fn global_shutdown() -> &'static CancellationToken {
GLOBAL_SHUTDOWN.get_or_init(CancellationToken::new)
}
#[must_use]
pub fn shutdown_token() -> CancellationToken {
global_shutdown().clone()
}
pub fn shutdown() {
global_shutdown().cancel();
}
pub struct Shutdown;
pub async fn race_shutdown<F, T>(fut: F) -> Result<T, Shutdown>
where
F: std::future::Future<Output = T>,
{
let token = shutdown_token();
tokio::select! {
result = fut => Ok(result),
() = token.cancelled() => Err(Shutdown),
}
}
#[must_use]
pub async fn sleep_or_shutdown(duration: Duration) -> bool {
race_shutdown(tokio::time::sleep(duration)).await.is_ok()
}
pub async fn wait_for_shutdown_signal() -> anyhow::Result<()> {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut sigint = signal(SignalKind::interrupt())?;
let mut sigterm = signal(SignalKind::terminate())?;
let mut sighup = signal(SignalKind::hangup())?;
loop {
tokio::select! {
_ = sigint.recv() => {
info!("Received SIGINT, shutting down...");
break;
}
_ = sigterm.recv() => {
info!("Received SIGTERM, shutting down...");
break;
}
_ = sighup.recv() => {
debug!("Received SIGHUP, ignoring (daemon stays running)");
}
}
}
}
#[cfg(not(unix))]
{
tokio::signal::ctrl_c().await?;
info!("Received Ctrl+C, shutting down...");
}
Ok(())
}
#[cfg(unix)]
pub fn install_fatal_signal_handlers() {
use std::sync::Once;
static INSTALLED: Once = Once::new();
INSTALLED.call_once(|| {
unsafe {
libc::signal(
libc::SIGBUS,
sigbus_handler as *const () as libc::sighandler_t,
);
libc::signal(
libc::SIGABRT,
sigabrt_handler as *const () as libc::sighandler_t,
);
}
});
}
const SIGBUS_MSG: &str = "mahbot: caught SIGBUS (bus error), terminating\n";
const SIGABRT_MSG: &str = "mahbot: caught SIGABRT (abort), terminating\n";
extern "C" fn sigbus_handler(_sig: i32) {
unsafe {
let _ = libc::write(
libc::STDERR_FILENO,
SIGBUS_MSG.as_ptr().cast::<libc::c_void>(),
SIGBUS_MSG.len(),
);
libc::_exit(1);
}
}
extern "C" fn sigabrt_handler(_sig: i32) {
unsafe {
let _ = libc::write(
libc::STDERR_FILENO,
SIGABRT_MSG.as_ptr().cast::<libc::c_void>(),
SIGABRT_MSG.len(),
);
libc::_exit(1);
}
}
#[cfg(not(unix))]
pub fn install_fatal_signal_handlers() {
}