Skip to main content

matrixcode_core/
cancel.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicBool, Ordering},
4};
5
6/// A token that can be used to cancel ongoing operations.
7/// Uses an atomic bool for reliable cancellation detection.
8pub struct CancellationToken {
9    cancelled: Arc<AtomicBool>,
10}
11
12impl CancellationToken {
13    /// Create a new cancellation token.
14    pub fn new() -> Self {
15        Self {
16            cancelled: Arc::new(AtomicBool::new(false)),
17        }
18    }
19
20    /// Cancel the operation. All clones will see this.
21    pub fn cancel(&self) {
22        self.cancelled.store(true, Ordering::SeqCst);
23    }
24
25    /// Check if the token has been cancelled.
26    pub fn is_cancelled(&self) -> bool {
27        self.cancelled.load(Ordering::SeqCst)
28    }
29
30    /// Reset the cancellation state. Allows reuse after interrupt.
31    /// All clones will see the reset state.
32    pub fn reset(&self) {
33        self.cancelled.store(false, Ordering::SeqCst);
34    }
35}
36
37impl Default for CancellationToken {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl Clone for CancellationToken {
44    fn clone(&self) -> Self {
45        Self {
46            cancelled: self.cancelled.clone(),
47        }
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn test_cancel() {
57        let token = CancellationToken::new();
58        assert!(!token.is_cancelled());
59        token.cancel();
60        assert!(token.is_cancelled());
61    }
62
63    #[test]
64    fn test_clone_cancel() {
65        let token = CancellationToken::new();
66        let clone = token.clone();
67        clone.cancel();
68        assert!(token.is_cancelled());
69        assert!(clone.is_cancelled());
70    }
71
72    #[test]
73    fn test_multiple_clones() {
74        let token = CancellationToken::new();
75        let clone1 = token.clone();
76        let clone2 = clone1.clone();
77
78        assert!(!token.is_cancelled());
79        assert!(!clone1.is_cancelled());
80        assert!(!clone2.is_cancelled());
81
82        token.cancel();
83
84        assert!(token.is_cancelled());
85        assert!(clone1.is_cancelled());
86        assert!(clone2.is_cancelled());
87    }
88
89    #[test]
90    fn test_reset() {
91        let token = CancellationToken::new();
92        token.cancel();
93        assert!(token.is_cancelled());
94        token.reset();
95        assert!(!token.is_cancelled());
96    }
97
98    #[test]
99    fn test_reset_visible_in_clones() {
100        let token = CancellationToken::new();
101        let clone = token.clone();
102
103        token.cancel();
104        assert!(clone.is_cancelled());
105
106        clone.reset();
107        assert!(!token.is_cancelled());
108        assert!(!clone.is_cancelled());
109    }
110
111    #[test]
112    fn test_cancel_reset_cycle() {
113        let token = CancellationToken::new();
114
115        // First cycle
116        token.cancel();
117        assert!(token.is_cancelled());
118        token.reset();
119        assert!(!token.is_cancelled());
120
121        // Second cycle
122        token.cancel();
123        assert!(token.is_cancelled());
124        token.reset();
125        assert!(!token.is_cancelled());
126
127        // Third cycle
128        token.cancel();
129        assert!(token.is_cancelled());
130    }
131}