use std::future::pending;
#[derive(Clone, Debug)]
pub struct ShutdownSignal {
tx: async_channel::Sender<()>,
}
impl ShutdownSignal {
pub fn shutdown(&self) {
self.tx.close();
}
pub fn is_shutdown(&self) -> bool {
self.tx.is_closed()
}
}
#[derive(Clone, Debug)]
pub struct Shutdown {
rx: Option<async_channel::Receiver<()>>,
}
impl Shutdown {
pub fn channel() -> (ShutdownSignal, Self) {
let (tx, rx) = async_channel::bounded(1);
(ShutdownSignal { tx }, Self { rx: Some(rx) })
}
pub fn never() -> Self {
Self { rx: None }
}
pub fn is_shutdown(&self) -> bool {
self.rx.as_ref().is_some_and(|rx| rx.is_closed())
}
pub async fn wait(&self) {
match &self.rx {
Some(rx) => while rx.recv().await.is_ok() {},
None => pending().await,
}
}
}
impl Default for Shutdown {
fn default() -> Self {
Self::never()
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_lite::future::{block_on, poll_once};
#[test]
fn wait_resolves_after_shutdown() {
let (signal, shutdown) = Shutdown::channel();
assert!(!shutdown.is_shutdown());
assert!(block_on(poll_once(shutdown.wait())).is_none());
signal.shutdown();
assert!(signal.is_shutdown());
assert!(shutdown.is_shutdown());
assert!(block_on(poll_once(shutdown.wait())).is_some());
}
#[test]
fn never_stays_pending() {
let shutdown = Shutdown::never();
assert!(!shutdown.is_shutdown());
assert!(block_on(poll_once(shutdown.wait())).is_none());
}
#[test]
fn shutdown_is_idempotent() {
let (signal, shutdown) = Shutdown::channel();
signal.shutdown();
signal.clone().shutdown();
assert!(block_on(poll_once(shutdown.wait())).is_some());
}
}