use std::{future::Future, pin::Pin, sync::Arc};
use tokio::sync::Notify;
#[derive(Debug, Clone)]
pub struct ShutdownToken {
inner: Arc<ShutdownState>,
}
#[derive(Debug)]
struct ShutdownState {
notify: Notify,
cancelled: std::sync::atomic::AtomicBool,
}
impl Default for ShutdownToken {
fn default() -> Self {
Self::new()
}
}
impl ShutdownToken {
pub fn new() -> Self {
Self {
inner: Arc::new(ShutdownState {
notify: Notify::new(),
cancelled: std::sync::atomic::AtomicBool::new(false),
}),
}
}
pub fn cancel(&self) {
if !self
.inner
.cancelled
.swap(true, std::sync::atomic::Ordering::SeqCst)
{
self.inner.notify.notify_waiters();
}
}
pub fn is_cancelled(&self) -> bool {
self.inner
.cancelled
.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn cancelled(&self) -> ShutdownFuture<'_> {
Box::pin(async move {
loop {
let notified = self.inner.notify.notified();
if self.is_cancelled() {
return;
}
notified.await;
}
})
}
}
pub type ShutdownFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
pub async fn shutdown_signal() {
if let Err(error) = tokio::signal::ctrl_c().await {
tracing::warn!(%error, "failed to listen for ctrl-c");
}
}