use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::graph::error::{GraphBuilderError, GraphResult};
#[derive(Clone, Debug, Default)]
pub struct CancellationToken {
flag: Arc<AtomicBool>,
}
impl CancellationToken {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn cancel(&self) {
self.flag.store(true, Ordering::Release);
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.flag.load(Ordering::Acquire)
}
pub fn check(&self) -> GraphResult<()> {
if self.is_cancelled() {
Err(GraphBuilderError::Cancelled)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cancellation_token_flag_starts_unset() {
let token = CancellationToken::new();
assert!(!token.is_cancelled());
}
#[test]
fn cancellation_token_cancel_sets_flag() {
let token = CancellationToken::new();
assert!(!token.is_cancelled());
token.cancel();
assert!(token.is_cancelled());
token.cancel();
assert!(token.is_cancelled());
}
#[test]
fn cancellation_token_check_returns_ok_when_not_cancelled() {
let token = CancellationToken::new();
assert!(token.check().is_ok());
}
#[test]
fn cancellation_token_check_returns_cancelled_when_cancelled() {
let token = CancellationToken::new();
token.cancel();
let err = token
.check()
.expect_err("cancelled token must return Err from check()");
assert!(
matches!(err, GraphBuilderError::Cancelled),
"expected GraphBuilderError::Cancelled, got: {err:?}"
);
}
#[test]
fn cancellation_token_clone_shares_state() {
let token = CancellationToken::new();
let clone = token.clone();
assert!(!token.is_cancelled());
assert!(!clone.is_cancelled());
clone.cancel();
assert!(token.is_cancelled());
assert!(clone.is_cancelled());
}
#[test]
fn cancellation_token_is_send_and_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<CancellationToken>();
assert_sync::<CancellationToken>();
}
}