use std::net::SocketAddr;
use std::sync::Arc;
use anyhow::Result;
use axum_server::Handle;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use crate::rpc::{self, RpcState};
use crate::telemetry;
pub fn graceful_shutdown(
state: Arc<RpcState>,
canceller: CancellationToken,
http_handle: Handle<SocketAddr>,
) -> JoinHandle<()> {
tokio::spawn(async move {
match listen().await {
Ok(signal) => {
warn!(target: super::LOG, "{signal} received. Waiting for a graceful shutdown. A second signal will force an immediate shutdown.");
}
_ => {
error!(target: super::LOG, "Failed to listen to shutdown signal. Terminating immediately.");
canceller.cancel();
}
}
let shutdown = {
let http_handle = http_handle.clone();
let canceller = canceller.clone();
let state = state.clone();
tokio::spawn(async move {
http_handle.graceful_shutdown(None);
while http_handle.connection_count() > 0 {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
rpc::graceful_shutdown(state).await;
canceller.cancel();
telemetry::shutdown();
})
};
tokio::select! {
biased;
_ = shutdown => (),
_ = canceller.cancelled() => {
http_handle.shutdown();
rpc::shutdown(state);
canceller.cancel();
telemetry::shutdown();
}
res = listen() => {
if let Ok(signal) = res {
warn!(target: super::LOG, "{signal} received during graceful shutdown. Terminating immediately.");
} else {
error!(target: super::LOG, "Failed to listen to shutdown signal. Terminating immediately.");
}
http_handle.shutdown();
rpc::shutdown(state);
canceller.cancel();
telemetry::shutdown();
},
}
})
}
#[cfg(unix)]
pub async fn listen() -> Result<String> {
info!(target: super::LOG, "Listening for a system shutdown signal.");
use tokio::signal::unix::{SignalKind, signal};
let mut sighup = signal(SignalKind::hangup())?;
let mut sigint = signal(SignalKind::interrupt())?;
let mut sigquit = signal(SignalKind::quit())?;
let mut sigterm = signal(SignalKind::terminate())?;
tokio::select! {
_ = sighup.recv() => {
Ok(String::from("SIGHUP"))
}
_ = sigint.recv() => {
Ok(String::from("SIGINT"))
}
_ = sigquit.recv() => {
Ok(String::from("SIGQUIT"))
}
_ = sigterm.recv() => {
Ok(String::from("SIGTERM"))
}
}
}
#[cfg(windows)]
pub async fn listen() -> Result<String> {
info!(target: super::LOG, "Listening for a system shutdown signal.");
use tokio::signal::windows;
let mut exit = windows::ctrl_c()?;
let mut leave = windows::ctrl_break()?;
let mut close = windows::ctrl_close()?;
let mut shutdown = windows::ctrl_shutdown()?;
tokio::select! {
_ = exit.recv() => {
Ok(String::from("CTRL-C"))
}
_ = leave.recv() => {
Ok(String::from("CTRL-BREAK"))
}
_ = close.recv() => {
Ok(String::from("CTRL-CLOSE"))
}
_ = shutdown.recv() => {
Ok(String::from("CTRL-SHUTDOWN"))
}
}
}