Skip to main content

indicatrix_dispatch/
cancel.rs

1//! [`CancelToken`]: one shared cancellation flag for an epoch or a job.
2
3use std::sync::{
4    Arc,
5    atomic::{AtomicBool, Ordering},
6};
7
8/// A cloneable cancellation flag. Every clone observes the same flag, so the owner of
9/// a job keeps one clone and hands others to the lanes working on it.
10///
11/// Cancellation is one-way: once raised it stays raised for the token's lifetime.
12#[derive(Debug, Clone, Default)]
13pub struct CancelToken(Arc<AtomicBool>);
14
15impl CancelToken {
16    /// A fresh, not-yet-cancelled token.
17    #[must_use]
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    /// Raises the flag for every clone of this token.
23    pub fn cancel(&self) {
24        self.0.store(true, Ordering::Release);
25    }
26
27    /// Whether [`Self::cancel`] has been called on any clone.
28    #[must_use]
29    pub fn is_cancelled(&self) -> bool {
30        self.0.load(Ordering::Acquire)
31    }
32
33    /// The underlying flag, for code that takes a plain `&AtomicBool` (the export's
34    /// existing dispatch helpers, the tracer's cancellable entry points).
35    #[must_use]
36    pub fn as_flag(&self) -> &AtomicBool {
37        &self.0
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::CancelToken;
44
45    #[test]
46    fn every_clone_sees_a_cancel() {
47        let token = CancelToken::new();
48        let clone = token.clone();
49        assert!(!clone.is_cancelled());
50        token.cancel();
51        assert!(clone.is_cancelled());
52        assert!(clone.as_flag().load(std::sync::atomic::Ordering::Relaxed));
53    }
54}