use std::sync::Arc;
use tokio::sync::Semaphore;
#[derive(Clone, Debug)]
pub struct CancelToken {
sem: Arc<Semaphore>,
}
impl Default for CancelToken {
fn default() -> Self {
Self::new()
}
}
impl CancelToken {
pub fn new() -> Self {
Self {
sem: Arc::new(Semaphore::new(0)),
}
}
pub fn cancel(&self) {
self.sem.close();
}
pub fn is_cancelled(&self) -> bool {
self.sem.is_closed()
}
pub async fn cancelled(&self) {
let _ = self.sem.acquire().await;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn a_live_token_never_resolves_and_a_cancelled_one_resolves_for_every_waiter() {
let t = CancelToken::new();
assert!(!t.is_cancelled());
assert!(
tokio::time::timeout(std::time::Duration::from_millis(50), t.cancelled())
.await
.is_err(),
"a live token resolved `cancelled()`"
);
let (a, b) = (t.clone(), t.clone());
let ha = tokio::spawn(async move { a.cancelled().await });
let hb = tokio::spawn(async move { b.cancelled().await });
t.cancel();
tokio::time::timeout(std::time::Duration::from_secs(5), ha)
.await
.expect("waiter a released")
.expect("waiter a not panicked");
tokio::time::timeout(std::time::Duration::from_secs(5), hb)
.await
.expect("waiter b released")
.expect("waiter b not panicked");
assert!(t.is_cancelled());
t.cancel();
tokio::time::timeout(std::time::Duration::from_secs(5), t.cancelled())
.await
.expect("an already-cancelled token resolves immediately");
}
}