use tokio::sync::oneshot;
pub struct Shutdown {
receiver: oneshot::Receiver<()>,
}
impl Shutdown {
pub fn new() -> (Self, ShutdownHandle) {
#[cfg(feature = "tracing")]
tracing::trace!("creating new shutdown channel");
let (tx, rx) = oneshot::channel();
(Self { receiver: rx }, ShutdownHandle { sender: tx })
}
pub async fn recv(self) {
#[cfg(feature = "tracing")]
tracing::debug!("waiting for shutdown signal");
let _ = self.receiver.await;
#[cfg(feature = "tracing")]
tracing::debug!("shutdown signal received");
}
}
pub struct ShutdownHandle {
sender: oneshot::Sender<()>,
}
impl ShutdownHandle {
pub fn shutdown(self) {
#[cfg(feature = "tracing")]
tracing::info!("triggering shutdown");
let _ = self.sender.send(());
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use tokio::time::timeout;
#[tokio::test]
async fn test_shutdown_trigger() {
let (shutdown, handle) = Shutdown::new();
let task = tokio::spawn(async move {
shutdown.recv().await;
});
handle.shutdown();
assert!(timeout(Duration::from_millis(100), task).await.is_ok());
}
#[tokio::test]
async fn test_shutdown_handle_drop() {
let (shutdown, handle) = Shutdown::new();
let task = tokio::spawn(async move {
shutdown.recv().await;
});
drop(handle);
assert!(timeout(Duration::from_millis(100), task).await.is_ok());
}
}