use std::sync::atomic::AtomicBool;
use std::panic;
use std::sync::Arc;
use std::sync::atomic::Ordering::Relaxed;
#[derive(Clone)]
pub struct Health {
healthy: Arc<AtomicBool>,
shutdown_tx: crate::signal::ShutdownTx,
}
impl Health {
pub fn new(shutdown_tx: crate::signal::ShutdownTx) -> Self {
let health = Self {
healthy: Arc::new(AtomicBool::new(true)),
shutdown_tx,
};
let healthy = health.healthy.clone();
let shutdown_tx = health.shutdown_tx.clone();
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
tracing::error!(%panic_info, "Panic has occurred. Moving to Unhealthy");
healthy.swap(false, Relaxed);
let _ = shutdown_tx.send(());
default_hook(panic_info);
}));
health
}
pub fn check_liveness(&self) -> bool {
self.healthy.load(Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn panic_hook() {
let (shutdown_tx, _shutdown_rx) = crate::signal::channel();
let health = Health::new(shutdown_tx);
assert!(health.check_liveness());
let _unused = std::panic::catch_unwind(|| {
panic!("oh no!");
});
assert!(!health.check_liveness());
}
}