Skip to main content

matrixcode_core/
cancel.rs

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