#[cfg(unix)]
struct ShutdownSignals {
interrupt: tokio::signal::unix::Signal,
terminate: tokio::signal::unix::Signal,
hangup: tokio::signal::unix::Signal,
}
#[cfg(unix)]
impl ShutdownSignals {
fn install() -> std::io::Result<Self> {
use tokio::signal::unix::{SignalKind, signal};
Ok(Self {
interrupt: signal(SignalKind::interrupt())?,
terminate: signal(SignalKind::terminate())?,
hangup: signal(SignalKind::hangup())?,
})
}
async fn recv(&mut self) {
tokio::select! {
_ = self.interrupt.recv() => {}
_ = self.terminate.recv() => {}
_ = self.hangup.recv() => {}
}
}
}
pub struct Shutdown {
#[cfg(unix)]
signals: Option<ShutdownSignals>,
}
impl Shutdown {
pub fn install() -> Self {
Self {
#[cfg(unix)]
signals: ShutdownSignals::install().ok(),
}
}
pub async fn recv(&mut self) {
#[cfg(unix)]
if let Some(signals) = self.signals.as_mut() {
signals.recv().await;
return;
}
std::future::pending().await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn the_shutdown_arm_does_not_fire_on_its_own() {
let mut shutdown = Shutdown::install();
let fired =
tokio::time::timeout(std::time::Duration::from_millis(50), shutdown.recv()).await;
assert!(
fired.is_err(),
"the shutdown arm resolved with no signal delivered"
);
}
}