1 | //! This mod provides the logic for the inner tree structure of the CancellationToken. |
2 | //! |
3 | //! CancellationTokens are only light handles with references to [`TreeNode`]. |
4 | //! All the logic is actually implemented in the [`TreeNode`]. |
5 | //! |
6 | //! A [`TreeNode`] is part of the cancellation tree and may have one parent and an arbitrary number of |
7 | //! children. |
8 | //! |
9 | //! A [`TreeNode`] can receive the request to perform a cancellation through a CancellationToken. |
10 | //! This cancellation request will cancel the node and all of its descendants. |
11 | //! |
12 | //! As soon as a node cannot get cancelled any more (because it was already cancelled or it has no |
13 | //! more CancellationTokens pointing to it any more), it gets removed from the tree, to keep the |
14 | //! tree as small as possible. |
15 | //! |
16 | //! # Invariants |
17 | //! |
18 | //! Those invariants shall be true at any time. |
19 | //! |
20 | //! 1. A node that has no parents and no handles can no longer be cancelled. |
21 | //! This is important during both cancellation and refcounting. |
22 | //! |
23 | //! 2. If node B *is* or *was* a child of node A, then node B was created *after* node A. |
24 | //! This is important for deadlock safety, as it is used for lock order. |
25 | //! Node B can only become the child of node A in two ways: |
26 | //! - being created with `child_node()`, in which case it is trivially true that |
27 | //! node A already existed when node B was created |
28 | //! - being moved A->C->B to A->B because node C was removed in `decrease_handle_refcount()` |
29 | //! or `cancel()`. In this case the invariant still holds, as B was younger than C, and C |
30 | //! was younger than A, therefore B is also younger than A. |
31 | //! |
32 | //! 3. If two nodes are both unlocked and node A is the parent of node B, then node B is a child of |
33 | //! node A. It is important to always restore that invariant before dropping the lock of a node. |
34 | //! |
35 | //! # Deadlock safety |
36 | //! |
37 | //! We always lock in the order of creation time. We can prove this through invariant #2. |
38 | //! Specifically, through invariant #2, we know that we always have to lock a parent |
39 | //! before its child. |
40 | //! |
41 | use crate::loom::sync::{Arc, Mutex, MutexGuard}; |
42 | |
43 | /// A node of the cancellation tree structure |
44 | /// |
45 | /// The actual data it holds is wrapped inside a mutex for synchronization. |
46 | pub(crate) struct TreeNode { |
47 | inner: Mutex<Inner>, |
48 | waker: tokio::sync::Notify, |
49 | } |
50 | impl TreeNode { |
51 | pub(crate) fn new() -> Self { |
52 | Self { |
53 | inner: Mutex::new(Inner { |
54 | parent: None, |
55 | parent_idx: 0, |
56 | children: vec![], |
57 | is_cancelled: false, |
58 | num_handles: 1, |
59 | }), |
60 | waker: tokio::sync::Notify::new(), |
61 | } |
62 | } |
63 | |
64 | pub(crate) fn notified(&self) -> tokio::sync::futures::Notified<'_> { |
65 | self.waker.notified() |
66 | } |
67 | } |
68 | |
69 | /// The data contained inside a TreeNode. |
70 | /// |
71 | /// This struct exists so that the data of the node can be wrapped |
72 | /// in a Mutex. |
73 | struct Inner { |
74 | parent: Option<Arc<TreeNode>>, |
75 | parent_idx: usize, |
76 | children: Vec<Arc<TreeNode>>, |
77 | is_cancelled: bool, |
78 | num_handles: usize, |
79 | } |
80 | |
81 | /// Returns whether or not the node is cancelled |
82 | pub(crate) fn is_cancelled(node: &Arc<TreeNode>) -> bool { |
83 | node.inner.lock().unwrap().is_cancelled |
84 | } |
85 | |
86 | /// Creates a child node |
87 | pub(crate) fn child_node(parent: &Arc<TreeNode>) -> Arc<TreeNode> { |
88 | let mut locked_parent = parent.inner.lock().unwrap(); |
89 | |
90 | // Do not register as child if we are already cancelled. |
91 | // Cancelled trees can never be uncancelled and therefore |
92 | // need no connection to parents or children any more. |
93 | if locked_parent.is_cancelled { |
94 | return Arc::new(TreeNode { |
95 | inner: Mutex::new(Inner { |
96 | parent: None, |
97 | parent_idx: 0, |
98 | children: vec![], |
99 | is_cancelled: true, |
100 | num_handles: 1, |
101 | }), |
102 | waker: tokio::sync::Notify::new(), |
103 | }); |
104 | } |
105 | |
106 | let child = Arc::new(TreeNode { |
107 | inner: Mutex::new(Inner { |
108 | parent: Some(parent.clone()), |
109 | parent_idx: locked_parent.children.len(), |
110 | children: vec![], |
111 | is_cancelled: false, |
112 | num_handles: 1, |
113 | }), |
114 | waker: tokio::sync::Notify::new(), |
115 | }); |
116 | |
117 | locked_parent.children.push(child.clone()); |
118 | |
119 | child |
120 | } |
121 | |
122 | /// Disconnects the given parent from all of its children. |
123 | /// |
124 | /// Takes a reference to [Inner] to make sure the parent is already locked. |
125 | fn disconnect_children(node: &mut Inner) { |
126 | for child in std::mem::take(&mut node.children) { |
127 | let mut locked_child = child.inner.lock().unwrap(); |
128 | locked_child.parent_idx = 0; |
129 | locked_child.parent = None; |
130 | } |
131 | } |
132 | |
133 | /// Figures out the parent of the node and locks the node and its parent atomically. |
134 | /// |
135 | /// The basic principle of preventing deadlocks in the tree is |
136 | /// that we always lock the parent first, and then the child. |
137 | /// For more info look at *deadlock safety* and *invariant #2*. |
138 | /// |
139 | /// Sadly, it's impossible to figure out the parent of a node without |
140 | /// locking it. To then achieve locking order consistency, the node |
141 | /// has to be unlocked before the parent gets locked. |
142 | /// This leaves a small window where we already assume that we know the parent, |
143 | /// but neither the parent nor the node is locked. Therefore, the parent could change. |
144 | /// |
145 | /// To prevent that this problem leaks into the rest of the code, it is abstracted |
146 | /// in this function. |
147 | /// |
148 | /// The locked child and optionally its locked parent, if a parent exists, get passed |
149 | /// to the `func` argument via (node, None) or (node, Some(parent)). |
150 | fn with_locked_node_and_parent<F, Ret>(node: &Arc<TreeNode>, func: F) -> Ret |
151 | where |
152 | F: FnOnce(MutexGuard<'_, Inner>, Option<MutexGuard<'_, Inner>>) -> Ret, |
153 | { |
154 | use std::sync::TryLockError; |
155 | |
156 | let mut locked_node = node.inner.lock().unwrap(); |
157 | |
158 | // Every time this fails, the number of ancestors of the node decreases, |
159 | // so the loop must succeed after a finite number of iterations. |
160 | loop { |
161 | // Look up the parent of the currently locked node. |
162 | let potential_parent = match locked_node.parent.as_ref() { |
163 | Some(potential_parent) => potential_parent.clone(), |
164 | None => return func(locked_node, None), |
165 | }; |
166 | |
167 | // Lock the parent. This may require unlocking the child first. |
168 | let locked_parent = match potential_parent.inner.try_lock() { |
169 | Ok(locked_parent) => locked_parent, |
170 | Err(TryLockError::WouldBlock) => { |
171 | drop(locked_node); |
172 | // Deadlock safety: |
173 | // |
174 | // Due to invariant #2, the potential parent must come before |
175 | // the child in the creation order. Therefore, we can safely |
176 | // lock the child while holding the parent lock. |
177 | let locked_parent = potential_parent.inner.lock().unwrap(); |
178 | locked_node = node.inner.lock().unwrap(); |
179 | locked_parent |
180 | } |
181 | Err(TryLockError::Poisoned(err)) => Err(err).unwrap(), |
182 | }; |
183 | |
184 | // If we unlocked the child, then the parent may have changed. Check |
185 | // that we still have the right parent. |
186 | if let Some(actual_parent) = locked_node.parent.as_ref() { |
187 | if Arc::ptr_eq(actual_parent, &potential_parent) { |
188 | return func(locked_node, Some(locked_parent)); |
189 | } |
190 | } |
191 | } |
192 | } |
193 | |
194 | /// Moves all children from `node` to `parent`. |
195 | /// |
196 | /// `parent` MUST have been a parent of the node when they both got locked, |
197 | /// otherwise there is a potential for a deadlock as invariant #2 would be violated. |
198 | /// |
199 | /// To acquire the locks for node and parent, use [with_locked_node_and_parent]. |
200 | fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) { |
201 | // Pre-allocate in the parent, for performance |
202 | parent.children.reserve(node.children.len()); |
203 | |
204 | for child in std::mem::take(&mut node.children) { |
205 | { |
206 | let mut child_locked = child.inner.lock().unwrap(); |
207 | child_locked.parent = node.parent.clone(); |
208 | child_locked.parent_idx = parent.children.len(); |
209 | } |
210 | parent.children.push(child); |
211 | } |
212 | } |
213 | |
214 | /// Removes a child from the parent. |
215 | /// |
216 | /// `parent` MUST be the parent of `node`. |
217 | /// To acquire the locks for node and parent, use [with_locked_node_and_parent]. |
218 | fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) { |
219 | // Query the position from where to remove a node |
220 | let pos = node.parent_idx; |
221 | node.parent = None; |
222 | node.parent_idx = 0; |
223 | |
224 | // Unlock node, so that only one child at a time is locked. |
225 | // Otherwise we would violate the lock order (see 'deadlock safety') as we |
226 | // don't know the creation order of the child nodes |
227 | drop(node); |
228 | |
229 | // If `node` is the last element in the list, we don't need any swapping |
230 | if parent.children.len() == pos + 1 { |
231 | parent.children.pop().unwrap(); |
232 | } else { |
233 | // If `node` is not the last element in the list, we need to |
234 | // replace it with the last element |
235 | let replacement_child = parent.children.pop().unwrap(); |
236 | replacement_child.inner.lock().unwrap().parent_idx = pos; |
237 | parent.children[pos] = replacement_child; |
238 | } |
239 | |
240 | let len = parent.children.len(); |
241 | if 4 * len <= parent.children.capacity() { |
242 | parent.children.shrink_to(2 * len); |
243 | } |
244 | } |
245 | |
246 | /// Increases the reference count of handles. |
247 | pub(crate) fn increase_handle_refcount(node: &Arc<TreeNode>) { |
248 | let mut locked_node = node.inner.lock().unwrap(); |
249 | |
250 | // Once no handles are left over, the node gets detached from the tree. |
251 | // There should never be a new handle once all handles are dropped. |
252 | assert!(locked_node.num_handles > 0); |
253 | |
254 | locked_node.num_handles += 1; |
255 | } |
256 | |
257 | /// Decreases the reference count of handles. |
258 | /// |
259 | /// Once no handle is left, we can remove the node from the |
260 | /// tree and connect its parent directly to its children. |
261 | pub(crate) fn decrease_handle_refcount(node: &Arc<TreeNode>) { |
262 | let num_handles = { |
263 | let mut locked_node = node.inner.lock().unwrap(); |
264 | locked_node.num_handles -= 1; |
265 | locked_node.num_handles |
266 | }; |
267 | |
268 | if num_handles == 0 { |
269 | with_locked_node_and_parent(node, |mut node, parent| { |
270 | // Remove the node from the tree |
271 | match parent { |
272 | Some(mut parent) => { |
273 | // As we want to remove ourselves from the tree, |
274 | // we have to move the children to the parent, so that |
275 | // they still receive the cancellation event without us. |
276 | // Moving them does not violate invariant #1. |
277 | move_children_to_parent(&mut node, &mut parent); |
278 | |
279 | // Remove the node from the parent |
280 | remove_child(&mut parent, node); |
281 | } |
282 | None => { |
283 | // Due to invariant #1, we can assume that our |
284 | // children can no longer be cancelled through us. |
285 | // (as we now have neither a parent nor handles) |
286 | // Therefore we can disconnect them. |
287 | disconnect_children(&mut node); |
288 | } |
289 | } |
290 | }); |
291 | } |
292 | } |
293 | |
294 | /// Cancels a node and its children. |
295 | pub(crate) fn cancel(node: &Arc<TreeNode>) { |
296 | let mut locked_node = node.inner.lock().unwrap(); |
297 | |
298 | if locked_node.is_cancelled { |
299 | return; |
300 | } |
301 | |
302 | // One by one, adopt grandchildren and then cancel and detach the child |
303 | while let Some(child) = locked_node.children.pop() { |
304 | // This can't deadlock because the mutex we are already |
305 | // holding is the parent of child. |
306 | let mut locked_child = child.inner.lock().unwrap(); |
307 | |
308 | // Detach the child from node |
309 | // No need to modify node.children, as the child already got removed with `.pop` |
310 | locked_child.parent = None; |
311 | locked_child.parent_idx = 0; |
312 | |
313 | // If child is already cancelled, detaching is enough |
314 | if locked_child.is_cancelled { |
315 | continue; |
316 | } |
317 | |
318 | // Cancel or adopt grandchildren |
319 | while let Some(grandchild) = locked_child.children.pop() { |
320 | // This can't deadlock because the two mutexes we are already |
321 | // holding is the parent and grandparent of grandchild. |
322 | let mut locked_grandchild = grandchild.inner.lock().unwrap(); |
323 | |
324 | // Detach the grandchild |
325 | locked_grandchild.parent = None; |
326 | locked_grandchild.parent_idx = 0; |
327 | |
328 | // If grandchild is already cancelled, detaching is enough |
329 | if locked_grandchild.is_cancelled { |
330 | continue; |
331 | } |
332 | |
333 | // For performance reasons, only adopt grandchildren that have children. |
334 | // Otherwise, just cancel them right away, no need for another iteration. |
335 | if locked_grandchild.children.is_empty() { |
336 | // Cancel the grandchild |
337 | locked_grandchild.is_cancelled = true; |
338 | locked_grandchild.children = Vec::new(); |
339 | drop(locked_grandchild); |
340 | grandchild.waker.notify_waiters(); |
341 | } else { |
342 | // Otherwise, adopt grandchild |
343 | locked_grandchild.parent = Some(node.clone()); |
344 | locked_grandchild.parent_idx = locked_node.children.len(); |
345 | drop(locked_grandchild); |
346 | locked_node.children.push(grandchild); |
347 | } |
348 | } |
349 | |
350 | // Cancel the child |
351 | locked_child.is_cancelled = true; |
352 | locked_child.children = Vec::new(); |
353 | drop(locked_child); |
354 | child.waker.notify_waiters(); |
355 | |
356 | // Now the child is cancelled and detached and all its children are adopted. |
357 | // Just continue until all (including adopted) children are cancelled and detached. |
358 | } |
359 | |
360 | // Cancel the node itself. |
361 | locked_node.is_cancelled = true; |
362 | locked_node.children = Vec::new(); |
363 | drop(locked_node); |
364 | node.waker.notify_waiters(); |
365 | } |
366 | |