use std::time::Duration;
use tokio::sync::watch;
#[derive(Clone)]
pub struct Shutdown {
rx: watch::Receiver<bool>,
}
impl Shutdown {
pub(crate) fn new(rx: watch::Receiver<bool>) -> Self {
Self { rx }
}
pub fn never() -> Self {
let (tx, rx) = watch::channel(false);
std::mem::forget(tx);
Self { rx }
}
pub fn is_triggered(&self) -> bool {
*self.rx.borrow()
}
pub async fn wait(&self) {
let mut rx = self.rx.clone();
loop {
if *rx.borrow_and_update() {
return;
}
if rx.changed().await.is_err() {
return;
}
}
}
pub async fn sleep(&self, duration: Duration) -> bool {
tokio::select! {
_ = tokio::time::sleep(duration) => !self.is_triggered(),
_ = self.wait() => false,
}
}
}
#[derive(Clone)]
pub struct Context {
pub(crate) state: std::sync::Arc<crate::request::StateMap>,
shutdown: Shutdown,
}
impl Context {
pub(crate) fn new(state: std::sync::Arc<crate::request::StateMap>, shutdown: Shutdown) -> Self {
Self { state, shutdown }
}
pub fn state<T: Send + Sync + 'static>(&self) -> crate::error::Result<&T> {
self.state.get::<T>().ok_or_else(|| {
crate::error::Error::internal(format!(
"State of type {} is not registered",
std::any::type_name::<T>()
))
})
}
pub fn shutdown(&self) -> Shutdown {
self.shutdown.clone()
}
pub async fn sleep(&self, duration: Duration) -> bool {
self.shutdown.sleep(duration).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn wait_sleep_and_trigger() {
let (tx, rx) = watch::channel(false);
let s = Shutdown::new(rx);
assert!(!s.is_triggered());
assert!(s.sleep(Duration::from_millis(5)).await);
let s2 = s.clone();
let waiter = tokio::spawn(async move { s2.wait().await });
tx.send(true).unwrap();
waiter.await.unwrap();
assert!(s.is_triggered());
s.wait().await;
assert!(!s.sleep(Duration::from_secs(60)).await);
}
#[tokio::test]
async fn sleep_is_interrupted_by_the_signal() {
let (tx, rx) = watch::channel(false);
let s = Shutdown::new(rx);
let sleeper = tokio::spawn({
let s = s.clone();
async move { s.sleep(Duration::from_secs(60)).await }
});
tokio::time::sleep(Duration::from_millis(20)).await;
tx.send(true).unwrap();
assert!(!tokio::time::timeout(Duration::from_secs(5), sleeper)
.await
.unwrap()
.unwrap());
}
#[tokio::test]
async fn never_does_not_fire() {
let s = Shutdown::never();
assert!(!s.is_triggered());
assert!(tokio::time::timeout(Duration::from_millis(30), s.wait())
.await
.is_err());
}
}