runifold_core/
cancellation.rs1use std::sync::{
2 Arc, Mutex, Weak,
3 atomic::{AtomicBool, Ordering},
4};
5
6use event_listener::Event;
7
8#[derive(Clone, Debug)]
13pub struct CancellationToken {
14 state: Arc<CancellationState>,
15}
16
17#[derive(Debug)]
18struct CancellationState {
19 cancelled: AtomicBool,
20 event: Event,
21 children: Mutex<Vec<Weak<CancellationState>>>,
22}
23
24impl CancellationToken {
25 pub fn new() -> Self {
27 Self {
28 state: Arc::new(CancellationState {
29 cancelled: AtomicBool::new(false),
30 event: Event::new(),
31 children: Mutex::new(Vec::new()),
32 }),
33 }
34 }
35
36 #[must_use]
38 pub fn child_token(&self) -> Self {
39 let mut children = self
40 .state
41 .children
42 .lock()
43 .unwrap_or_else(std::sync::PoisonError::into_inner);
44 let child = Self {
45 state: Arc::new(CancellationState {
46 cancelled: AtomicBool::new(self.is_cancelled()),
47 event: Event::new(),
48 children: Mutex::new(Vec::new()),
49 }),
50 };
51 if !child.is_cancelled() {
52 children.push(Arc::downgrade(&child.state));
53 }
54 child
55 }
56
57 pub fn cancel(&self) {
59 cancel_state(&self.state);
60 }
61
62 pub fn is_cancelled(&self) -> bool {
64 self.state.cancelled.load(Ordering::Acquire)
65 }
66
67 pub async fn cancelled(&self) {
69 loop {
70 if self.is_cancelled() {
71 return;
72 }
73 let listener = self.state.event.listen();
74 if self.is_cancelled() {
75 return;
76 }
77 listener.await;
78 }
79 }
80}
81
82fn cancel_state(state: &Arc<CancellationState>) {
83 if state.cancelled.swap(true, Ordering::AcqRel) {
84 return;
85 }
86 state.event.notify(usize::MAX);
87
88 let children = state
89 .children
90 .lock()
91 .unwrap_or_else(std::sync::PoisonError::into_inner)
92 .iter()
93 .filter_map(Weak::upgrade)
94 .collect::<Vec<_>>();
95 for child in children {
96 cancel_state(&child);
97 }
98}
99
100impl Default for CancellationToken {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::CancellationToken;
109
110 #[test]
111 fn parent_cancellation_reaches_descendants() {
112 let parent = CancellationToken::new();
113 let child = parent.child_token();
114 let grandchild = child.child_token();
115
116 parent.cancel();
117
118 assert!(parent.is_cancelled());
119 assert!(child.is_cancelled());
120 assert!(grandchild.is_cancelled());
121 }
122
123 #[test]
124 fn child_cancellation_is_isolated() {
125 let parent = CancellationToken::new();
126 let first_child = parent.child_token();
127 let second_child = parent.child_token();
128
129 first_child.cancel();
130
131 assert!(first_child.is_cancelled());
132 assert!(!parent.is_cancelled());
133 assert!(!second_child.is_cancelled());
134 }
135
136 #[test]
137 fn asynchronous_waiters_are_woken_by_ancestor_cancellation() {
138 let parent = CancellationToken::new();
139 let child = parent.child_token();
140 let waiter = child.clone();
141 let canceller = std::thread::spawn(move || parent.cancel());
142
143 futures_executor::block_on(waiter.cancelled());
144 canceller.join().unwrap();
145
146 assert!(child.is_cancelled());
147 }
148
149 #[test]
150 fn children_created_after_cancellation_start_cancelled() {
151 let parent = CancellationToken::new();
152 parent.cancel();
153
154 let child = parent.child_token();
155
156 assert!(child.is_cancelled());
157 futures_executor::block_on(child.cancelled());
158 }
159}