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: Arc in std::mem::take(&mut node.children) { |
127 | let mut locked_child: MutexGuard<'_, Inner> = 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 | let mut potential_parent = { |
155 | let locked_node = node.inner.lock().unwrap(); |
156 | match locked_node.parent.clone() { |
157 | Some(parent) => parent, |
158 | // If we locked the node and its parent is `None`, we are in a valid state |
159 | // and can return. |
160 | None => return func(locked_node, None), |
161 | } |
162 | }; |
163 | |
164 | loop { |
165 | // Deadlock safety: |
166 | // |
167 | // Due to invariant #2, we know that we have to lock the parent first, and then the child. |
168 | // This is true even if the potential_parent is no longer the current parent or even its |
169 | // sibling, as the invariant still holds. |
170 | let locked_parent = potential_parent.inner.lock().unwrap(); |
171 | let locked_node = node.inner.lock().unwrap(); |
172 | |
173 | let actual_parent = match locked_node.parent.clone() { |
174 | Some(parent) => parent, |
175 | // If we locked the node and its parent is `None`, we are in a valid state |
176 | // and can return. |
177 | None => { |
178 | // Was the wrong parent, so unlock it before calling `func` |
179 | drop(locked_parent); |
180 | return func(locked_node, None); |
181 | } |
182 | }; |
183 | |
184 | // Loop until we managed to lock both the node and its parent |
185 | if Arc::ptr_eq(&actual_parent, &potential_parent) { |
186 | return func(locked_node, Some(locked_parent)); |
187 | } |
188 | |
189 | // Drop locked_parent before reassigning to potential_parent, |
190 | // as potential_parent is borrowed in it |
191 | drop(locked_node); |
192 | drop(locked_parent); |
193 | |
194 | potential_parent = actual_parent; |
195 | } |
196 | } |
197 | |
198 | /// Moves all children from `node` to `parent`. |
199 | /// |
200 | /// `parent` MUST have been a parent of the node when they both got locked, |
201 | /// otherwise there is a potential for a deadlock as invariant #2 would be violated. |
202 | /// |
203 | /// To aquire the locks for node and parent, use [with_locked_node_and_parent]. |
204 | fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) { |
205 | // Pre-allocate in the parent, for performance |
206 | parent.children.reserve(additional:node.children.len()); |
207 | |
208 | for child: Arc in std::mem::take(&mut node.children) { |
209 | { |
210 | let mut child_locked: MutexGuard<'_, Inner> = child.inner.lock().unwrap(); |
211 | child_locked.parent = node.parent.clone(); |
212 | child_locked.parent_idx = parent.children.len(); |
213 | } |
214 | parent.children.push(child); |
215 | } |
216 | } |
217 | |
218 | /// Removes a child from the parent. |
219 | /// |
220 | /// `parent` MUST be the parent of `node`. |
221 | /// To aquire the locks for node and parent, use [with_locked_node_and_parent]. |
222 | fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) { |
223 | // Query the position from where to remove a node |
224 | let pos = node.parent_idx; |
225 | node.parent = None; |
226 | node.parent_idx = 0; |
227 | |
228 | // Unlock node, so that only one child at a time is locked. |
229 | // Otherwise we would violate the lock order (see 'deadlock safety') as we |
230 | // don't know the creation order of the child nodes |
231 | drop(node); |
232 | |
233 | // If `node` is the last element in the list, we don't need any swapping |
234 | if parent.children.len() == pos + 1 { |
235 | parent.children.pop().unwrap(); |
236 | } else { |
237 | // If `node` is not the last element in the list, we need to |
238 | // replace it with the last element |
239 | let replacement_child = parent.children.pop().unwrap(); |
240 | replacement_child.inner.lock().unwrap().parent_idx = pos; |
241 | parent.children[pos] = replacement_child; |
242 | } |
243 | |
244 | let len = parent.children.len(); |
245 | if 4 * len <= parent.children.capacity() { |
246 | // equal to: |
247 | // parent.children.shrink_to(2 * len); |
248 | // but shrink_to was not yet stabilized in our minimal compatible version |
249 | let old_children = std::mem::replace(&mut parent.children, Vec::with_capacity(2 * len)); |
250 | parent.children.extend(old_children); |
251 | } |
252 | } |
253 | |
254 | /// Increases the reference count of handles. |
255 | pub(crate) fn increase_handle_refcount(node: &Arc<TreeNode>) { |
256 | let mut locked_node: MutexGuard<'_, Inner> = node.inner.lock().unwrap(); |
257 | |
258 | // Once no handles are left over, the node gets detached from the tree. |
259 | // There should never be a new handle once all handles are dropped. |
260 | assert!(locked_node.num_handles > 0); |
261 | |
262 | locked_node.num_handles += 1; |
263 | } |
264 | |
265 | /// Decreases the reference count of handles. |
266 | /// |
267 | /// Once no handle is left, we can remove the node from the |
268 | /// tree and connect its parent directly to its children. |
269 | pub(crate) fn decrease_handle_refcount(node: &Arc<TreeNode>) { |
270 | let num_handles = { |
271 | let mut locked_node = node.inner.lock().unwrap(); |
272 | locked_node.num_handles -= 1; |
273 | locked_node.num_handles |
274 | }; |
275 | |
276 | if num_handles == 0 { |
277 | with_locked_node_and_parent(node, |mut node, parent| { |
278 | // Remove the node from the tree |
279 | match parent { |
280 | Some(mut parent) => { |
281 | // As we want to remove ourselves from the tree, |
282 | // we have to move the children to the parent, so that |
283 | // they still receive the cancellation event without us. |
284 | // Moving them does not violate invariant #1. |
285 | move_children_to_parent(&mut node, &mut parent); |
286 | |
287 | // Remove the node from the parent |
288 | remove_child(&mut parent, node); |
289 | } |
290 | None => { |
291 | // Due to invariant #1, we can assume that our |
292 | // children can no longer be cancelled through us. |
293 | // (as we now have neither a parent nor handles) |
294 | // Therefore we can disconnect them. |
295 | disconnect_children(&mut node); |
296 | } |
297 | } |
298 | }); |
299 | } |
300 | } |
301 | |
302 | /// Cancels a node and its children. |
303 | pub(crate) fn cancel(node: &Arc<TreeNode>) { |
304 | let mut locked_node = node.inner.lock().unwrap(); |
305 | |
306 | if locked_node.is_cancelled { |
307 | return; |
308 | } |
309 | |
310 | // One by one, adopt grandchildren and then cancel and detach the child |
311 | while let Some(child) = locked_node.children.pop() { |
312 | // This can't deadlock because the mutex we are already |
313 | // holding is the parent of child. |
314 | let mut locked_child = child.inner.lock().unwrap(); |
315 | |
316 | // Detach the child from node |
317 | // No need to modify node.children, as the child already got removed with `.pop` |
318 | locked_child.parent = None; |
319 | locked_child.parent_idx = 0; |
320 | |
321 | // If child is already cancelled, detaching is enough |
322 | if locked_child.is_cancelled { |
323 | continue; |
324 | } |
325 | |
326 | // Cancel or adopt grandchildren |
327 | while let Some(grandchild) = locked_child.children.pop() { |
328 | // This can't deadlock because the two mutexes we are already |
329 | // holding is the parent and grandparent of grandchild. |
330 | let mut locked_grandchild = grandchild.inner.lock().unwrap(); |
331 | |
332 | // Detach the grandchild |
333 | locked_grandchild.parent = None; |
334 | locked_grandchild.parent_idx = 0; |
335 | |
336 | // If grandchild is already cancelled, detaching is enough |
337 | if locked_grandchild.is_cancelled { |
338 | continue; |
339 | } |
340 | |
341 | // For performance reasons, only adopt grandchildren that have children. |
342 | // Otherwise, just cancel them right away, no need for another iteration. |
343 | if locked_grandchild.children.is_empty() { |
344 | // Cancel the grandchild |
345 | locked_grandchild.is_cancelled = true; |
346 | locked_grandchild.children = Vec::new(); |
347 | drop(locked_grandchild); |
348 | grandchild.waker.notify_waiters(); |
349 | } else { |
350 | // Otherwise, adopt grandchild |
351 | locked_grandchild.parent = Some(node.clone()); |
352 | locked_grandchild.parent_idx = locked_node.children.len(); |
353 | drop(locked_grandchild); |
354 | locked_node.children.push(grandchild); |
355 | } |
356 | } |
357 | |
358 | // Cancel the child |
359 | locked_child.is_cancelled = true; |
360 | locked_child.children = Vec::new(); |
361 | drop(locked_child); |
362 | child.waker.notify_waiters(); |
363 | |
364 | // Now the child is cancelled and detached and all its children are adopted. |
365 | // Just continue until all (including adopted) children are cancelled and detached. |
366 | } |
367 | |
368 | // Cancel the node itself. |
369 | locked_node.is_cancelled = true; |
370 | locked_node.children = Vec::new(); |
371 | drop(locked_node); |
372 | node.waker.notify_waiters(); |
373 | } |
374 | |