reth_revm/
cancelled.rs

1use alloc::sync::Arc;
2use core::sync::atomic::AtomicBool;
3
4/// A marker that can be used to cancel execution.
5///
6/// If dropped, it will set the `cancelled` flag to true.
7///
8/// This is most useful when a payload job needs to be cancelled.
9#[derive(Default, Clone, Debug)]
10pub struct CancelOnDrop(Arc<AtomicBool>);
11
12// === impl CancelOnDrop ===
13
14impl CancelOnDrop {
15    /// Returns true if the job was cancelled.
16    pub fn is_cancelled(&self) -> bool {
17        self.0.load(core::sync::atomic::Ordering::Relaxed)
18    }
19}
20
21impl Drop for CancelOnDrop {
22    fn drop(&mut self) {
23        self.0.store(true, core::sync::atomic::Ordering::Relaxed);
24    }
25}
26
27/// A marker that can be used to cancel execution.
28///
29/// If dropped, it will NOT set the `cancelled` flag to true.
30/// If `cancel` is called, the `cancelled` flag will be set to true.
31///
32/// This is useful in prewarming, when an external signal is received to cancel many prewarming
33/// tasks.
34#[derive(Default, Clone, Debug)]
35pub struct ManualCancel(Arc<AtomicBool>);
36
37// === impl ManualCancel ===
38
39impl ManualCancel {
40    /// Returns true if the job was cancelled.
41    pub fn is_cancelled(&self) -> bool {
42        self.0.load(core::sync::atomic::Ordering::Relaxed)
43    }
44
45    /// Drops the [`ManualCancel`], setting the cancelled flag to true.
46    pub fn cancel(self) {
47        self.0.store(true, core::sync::atomic::Ordering::Relaxed);
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn test_default_cancelled() {
57        let c = CancelOnDrop::default();
58        assert!(!c.is_cancelled());
59    }
60
61    #[test]
62    fn test_default_cancel_task() {
63        let c = ManualCancel::default();
64        assert!(!c.is_cancelled());
65    }
66
67    #[test]
68    fn test_set_cancel_task() {
69        let c = ManualCancel::default();
70        assert!(!c.is_cancelled());
71        let c2 = c.clone();
72        let c3 = c.clone();
73        c.cancel();
74        assert!(c3.is_cancelled());
75        assert!(c2.is_cancelled());
76    }
77
78    #[test]
79    fn test_cancel_task_multiple_threads() {
80        let c = ManualCancel::default();
81        let cloned_cancel = c.clone();
82
83        // we want to make sure that:
84        // * we can spawn tasks that do things
85        // * those tasks can run to completion and the flag remains unset unless we call cancel
86        let mut handles = vec![];
87        for _ in 0..10 {
88            let c = c.clone();
89            let handle = std::thread::spawn(move || {
90                for _ in 0..1000 {
91                    if c.is_cancelled() {
92                        return;
93                    }
94                }
95            });
96            handles.push(handle);
97        }
98
99        // wait for all the threads to finish
100        for handle in handles {
101            handle.join().unwrap();
102        }
103
104        // check that the flag is still unset
105        assert!(!c.is_cancelled());
106
107        // cancel and check that the flag is set
108        c.cancel();
109        assert!(cloned_cancel.is_cancelled());
110    }
111}