use std::sync::OnceLock;
use std::sync::atomic::Ordering;
use tokio::signal;
use tokio::sync::watch;
use tracing::{info, warn};
struct ShutdownWatch {
tx: watch::Sender<bool>,
_rx: watch::Receiver<bool>,
}
static SHUTDOWN: OnceLock<ShutdownWatch> = OnceLock::new();
fn shutdown_watch() -> &'static ShutdownWatch {
SHUTDOWN.get_or_init(|| {
let (tx, rx) = watch::channel(false);
let notify_tx = tx.clone();
tokio::spawn(async move {
wait_for_shutdown_signal().await;
#[cfg(feature = "production")]
crate::node::parallel_ibd::IBD_SHUTDOWN_REQUESTED.store(true, Ordering::Release);
let _ = notify_tx.send(true);
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
warn!("Shutdown grace period elapsed — forcing process exit");
std::process::exit(0);
});
ShutdownWatch { tx, _rx: rx }
})
}
pub async fn wait_for_shutdown_signal() {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut sigterm = match signal(SignalKind::terminate()) {
Ok(s) => s,
Err(e) => {
warn!("Failed to register SIGTERM handler: {}", e);
signal::ctrl_c().await.ok();
return;
}
};
let mut sigint = match signal(SignalKind::interrupt()) {
Ok(s) => s,
Err(e) => {
warn!("Failed to register SIGINT handler: {}", e);
signal::ctrl_c().await.ok();
return;
}
};
tokio::select! {
_ = sigterm.recv() => {
info!("Received SIGTERM, shutting down gracefully...");
}
_ = sigint.recv() => {
info!("Received SIGINT, shutting down gracefully...");
}
_ = signal::ctrl_c() => {
info!("Received Ctrl+C, shutting down gracefully...");
}
}
}
#[cfg(not(unix))]
{
match signal::ctrl_c().await {
Ok(()) => {
info!("Received Ctrl+C, shutting down gracefully...");
}
Err(e) => {
warn!("Failed to listen for shutdown signal: {}", e);
}
}
}
}
pub async fn shutdown_signal() {
wait_for_shutdown_signal().await;
}
pub fn create_shutdown_receiver() -> watch::Receiver<bool> {
shutdown_watch().tx.subscribe()
}
pub fn shutdown_requested() -> bool {
*shutdown_watch().tx.borrow()
}