use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Clone, Debug, Default)]
pub struct CancelToken(Arc<AtomicBool>);
impl CancelToken {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn cancel(&self) {
self.0.store(true, Ordering::Relaxed);
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.0.load(Ordering::Relaxed)
}
pub fn reset(&self) {
self.0.store(false, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_token_is_not_cancelled() {
let t = CancelToken::new();
assert!(!t.is_cancelled());
}
#[test]
fn cancel_flips_the_flag() {
let t = CancelToken::new();
t.cancel();
assert!(t.is_cancelled());
}
#[test]
fn cancel_is_visible_across_clones() {
let a = CancelToken::new();
let b = a.clone();
assert!(!b.is_cancelled());
a.cancel();
assert!(
b.is_cancelled(),
"cancel on one handle must surface on the clone"
);
}
#[test]
fn cancel_is_idempotent() {
let t = CancelToken::new();
t.cancel();
t.cancel();
assert!(t.is_cancelled());
}
#[test]
fn reset_clears_the_flag() {
let t = CancelToken::new();
t.cancel();
assert!(t.is_cancelled());
t.reset();
assert!(!t.is_cancelled());
}
#[test]
fn reset_is_visible_across_clones() {
let a = CancelToken::new();
let b = a.clone();
a.cancel();
assert!(b.is_cancelled());
b.reset();
assert!(
!a.is_cancelled(),
"reset on one handle must surface on the clone"
);
}
#[test]
fn cancel_propagates_between_threads() {
let t = CancelToken::new();
let bg = t.clone();
let handle = std::thread::spawn(move || {
bg.cancel();
});
handle.join().unwrap();
assert!(t.is_cancelled());
}
#[test]
fn token_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<CancelToken>();
}
}