use std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
time::Duration,
};
use tokio::{sync::Notify, time::timeout};
#[derive(Clone)]
pub struct ShutdownSwitch {
toggle: Arc<AtomicBool>,
notification: Arc<Notify>,
}
impl ShutdownSwitch {
pub fn new() -> Self {
Self {
toggle: Arc::new(false.into()),
notification: Default::default(),
}
}
pub fn shutdown(&self) {
self.toggle.store(true, Ordering::Relaxed);
self.notification.notify_waiters();
}
pub fn running(&self) -> bool {
!self.toggle.load(Ordering::Relaxed)
}
async fn block_until_shutdown(&self) {
let cooldown = Duration::from_secs(1);
while self.running() {
let _ = timeout(cooldown, self.notification.notified()).await;
}
}
pub async fn sleep(&self, duration: Duration) -> ShutdownSleep {
if !self.running() {
return ShutdownSleep::Shutdown;
}
match timeout(duration, self.block_until_shutdown()).await {
Ok(_) => ShutdownSleep::Shutdown,
Err(_) => ShutdownSleep::Slept,
}
}
}
pub enum ShutdownSleep {
Shutdown,
Slept,
}
impl Drop for ShutdownSwitch {
fn drop(&mut self) {
self.shutdown();
}
}