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 {
new() -> Self51 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
notified(&self) -> tokio::sync::futures::Notified<'_>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
is_cancelled(node: &Arc<TreeNode>) -> bool82 pub(crate) fn is_cancelled(node: &Arc<TreeNode>) -> bool {
83 node.inner.lock().unwrap().is_cancelled
84 }
85
86 /// Creates a child node
child_node(parent: &Arc<TreeNode>) -> Arc<TreeNode>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.
disconnect_children(node: &mut Inner)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)).
with_locked_node_and_parent<F, Ret>(node: &Arc<TreeNode>, func: F) -> Ret where F: FnOnce(MutexGuard<'_, Inner>, Option<MutexGuard<'_, Inner>>) -> Ret,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].
move_children_to_parent(node: &mut Inner, parent: &mut Inner)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].
remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>)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.
increase_handle_refcount(node: &Arc<TreeNode>)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.
decrease_handle_refcount(node: &Arc<TreeNode>)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.
cancel(node: &Arc<TreeNode>)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