async-backtrace 0.2.2

Efficient, logical 'backtraces' of async tasks.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
use std::{iter::FusedIterator, marker::PhantomPinned, pin::Pin, ptr::NonNull};

use crate::{
    cell::{Cell, UnsafeCell},
    linked_list,
    sync::Mutex,
    Location,
};

pin_project_lite::pin_project! {
/// A [`Location`] in an intrusive, doubly-linked tree of [`Location`]s.
pub struct Frame {
    // The location associated with this frame.
    location: Location,

    // The kind of this frame — either a root or a node.
    kind: Kind,

    // The children of this frame.
    children: UnsafeCell<Children>,

    // The siblings of this frame.
    #[pin]
    siblings: Siblings,

    // Since `Frame` is part of an intrusive linked list, it must remain pinned.
    _pinned: PhantomPinned,
}

impl PinnedDrop for Frame {
    fn drop(this: Pin<&mut Self>) {
        // If this frame has not yet been initialized, there's no need to do anything special upon drop.
        if this.is_uninitialized() {
            return;
        }

        let this = this.into_ref().get_ref();

        if let Some(parent) = this.parent() {
            // remove this frame as a child of its parent
            unsafe {
                parent.children.with_mut(|children| (*children).remove(this.into()));
            }
        } else {
            // this is a task; deregister it
            crate::tasks::deregister(this);
        }
    }
}
}

// It is safe to transfer a `Frame` across thread boundaries, as it does not
// contain any pointers to thread-local storage, nor does it enable interior
// mutation on shared pointers without locking.
unsafe impl Send for Frame {}

mod active_frame {
    use super::Frame;
    use crate::cell::Cell;
    use core::ptr::NonNull;

    #[cfg(loom)]
    loom::thread_local! {
        /// The [`Frame`] of the currently-executing [traced future](crate::Traced) (if any).
        static ACTIVE_FRAME: crate::cell::Cell<Option<NonNull<Frame>>> = Cell::new(None);
    }

    #[cfg(not(loom))]
    std::thread_local! {
        /// The [`Frame`] of the currently-executing [traced future](crate::Traced) (if any).
        static ACTIVE_FRAME: crate::cell::Cell<Option<NonNull<Frame>>> = const { Cell::new(None) };
    }

    /// By calling this function, you pinky-swear to ensure that the value of
    /// `ACTIVE_FRAME` is always a valid (dereferenceable) `NonNull<Frame>`.
    pub(crate) unsafe fn with<F, R>(f: F) -> R
    where
        F: FnOnce(&Cell<Option<NonNull<Frame>>>) -> R,
    {
        ACTIVE_FRAME.with(f)
    }
}

/// The kind of a [`Frame`].
enum Kind {
    /// The frame is not yet initialized.
    Uninitialized,

    /// The frame is the root node in its tree.
    Root {
        /// This mutex must be locked when modifying the
        /// [children][Frame::children] or [siblings][Frame::siblings] of this
        /// frame.
        mutex: Mutex<()>,
    },
    /// The frame is *not* the root node of its tree.
    Node {
        /// The parent of this frame.
        parent: NonNull<Frame>,
    },
}

/// The siblings of a frame.
type Siblings = linked_list::Pointers<Frame>;

/// The children of a frame.
type Children = linked_list::LinkedList<Frame, <Frame as linked_list::Link>::Target>;

impl Frame {
    /// Construct a new, uninitialized `Frame`.
    pub fn new(location: Location) -> Self {
        Self {
            location,
            kind: Kind::Uninitialized,
            children: UnsafeCell::new(linked_list::LinkedList::new()),
            siblings: linked_list::Pointers::new(),
            _pinned: PhantomPinned,
        }
    }

    /// Runs a given function on this frame.
    ///
    /// If an invocation of `Frame::in_scope` is nested within `f`, those frames
    /// will be initialized with this frame as their parent.
    pub fn in_scope<F, R>(self: Pin<&mut Self>, f: F) -> R
    where
        F: FnOnce() -> R,
    {
        // This non-generic preparation routine has been factored out of `in_scope`'s
        // body, so as to reduce the monomorphization burden on the compiler.
        //
        // The soundness of other routines in this module depend on this function *not*
        // being leaked from `in_scope`. In general, the drop-guard pattern cannot
        // safely and soundly be used for frame management. If we attempt to provide
        // such an API, we must ensure that unsoudness does not occur if child frames
        // are dropped before their parents, or if a drop-guard is held across an
        // `await` point.
        unsafe fn activate<'a>(
            mut frame: Pin<&'a mut Frame>,
            active: &'a Cell<Option<NonNull<Frame>>>,
        ) -> impl Drop + 'a {
            // If needed, initialize this frame.
            if frame.is_uninitialized() {
                let maybe_parent = active.get().map(|parent| parent.as_ref());
                frame.as_mut().initialize_unchecked(maybe_parent)
            }

            let frame = frame.into_ref().get_ref();

            // If this is the root frame, lock its children. This lock is inherited by
            // `f()`.
            let maybe_mutex_guard = if let Kind::Root { mutex } = &frame.kind {
                // Ignore poisoning. This is fine, since absolutely nothing between this line,
                // and the execution of `drop(maybe_mutex_guard)` can unwind-panic, *except* for
                // the execution of the user-provided function `f`. An unwind-panic of `f` will
                // not make this crate's state inconsistent, since the parent frame is always
                // restored by the below invocation of `crate::defer` upon its drop.
                Some(match mutex.lock() {
                    Ok(guard) => guard,
                    Err(err) => err.into_inner(),
                })
            } else {
                None
            };

            // Replace the previously-active frame with this frame.
            let previously_active = active.replace(Some(frame.into()));

            // At the end of this scope, restore the previously-active frame.
            crate::defer(move || {
                active.set(previously_active);
                drop(maybe_mutex_guard);
            })
        }

        unsafe {
            // SAFETY: We uphold `with`'s invariants by restoring the previously active
            // frame after the execution of `f()`.
            active_frame::with(|active| {
                // Activate this frame.
                let _restore = activate(self, active);
                // Finally, execute the given function.
                f()
            })
        }
    }

    /// Produces a boxed slice over this frame's ancestors.
    pub fn backtrace_locations(&self) -> Box<[Location]> {
        let len = self.backtrace().count();
        let mut vec = Vec::with_capacity(len);
        vec.extend(self.backtrace().map(Frame::location));
        vec.into_boxed_slice()
    }

    /// Produces the [`Location`] associated with this frame.
    pub fn location(&self) -> Location {
        self.location
    }

    /// Produces `true` if this `Frame` is uninitialized, otherwise false.
    fn is_uninitialized(&self) -> bool {
        self.kind.is_uninitialized()
    }

    /// Initializes this frame, unconditionally.
    ///
    /// ## Safety
    /// This method must only be called, at most, once.
    #[inline(never)]
    unsafe fn initialize_unchecked(mut self: Pin<&mut Self>, maybe_parent: Option<&Frame>) {
        match maybe_parent {
            // This frame has no parent...
            None => {
                // ...it is the root of its tree,
                *self.as_mut().project().kind = Kind::root();
                // ...and must be registered as a task.
                crate::tasks::register(self.into_ref().get_ref());
            }
            // This frame has a parent...
            Some(parent) => {
                // ...it is not the root of its tree.
                *self.as_mut().project().kind = Kind::node(parent);
                // ...and its parent should be notified that is has a new child.
                let this = NonNull::from(self.into_ref().get_ref());
                parent
                    .children
                    .with_mut(|children| (*children).push_front(this));
            }
        };
    }

    /// Executes the given function with a reference to the active frame on this
    /// thread (if any).
    pub fn with_active<F, R>(f: F) -> R
    where
        F: FnOnce(Option<&Frame>) -> R,
    {
        Frame::with_active_cell(|cell| f(cell.get()))
    }

    pub(crate) fn with_active_cell<F, R>(f: F) -> R
    where
        F: FnOnce(&Cell<Option<&Frame>>) -> R,
    {
        unsafe fn into_ref<'a, 'b>(
            cell: &'a Cell<Option<NonNull<Frame>>>,
        ) -> &'a Cell<Option<&'b Frame>> {
            // SAFETY: `Cell<NonNull<Frame>>` has the same layout has `Cell<&Frame>`,
            // because both `Cell` and `NonNull` are `#[repr(transparent)]`, and because
            // `*const Frame` has the same layout as `&Frame`.
            core::mem::transmute(cell)
        }

        unsafe {
            // SAFETY: We uphold `with`'s invariants, by only providing `f` with a
            // *reference* to the frame.
            active_frame::with(|cell| {
                let cell = into_ref(cell);
                f(cell)
            })
        }
    }

    /// Produces the mutex (if any) guarding this frame's children.
    pub(crate) fn mutex(&self) -> Option<&Mutex<()>> {
        if let Kind::Root { mutex } = &self.kind {
            Some(mutex)
        } else {
            None
        }
    }

    pub(crate) unsafe fn fmt<W: core::fmt::Write>(
        &self,
        w: &mut W,
        subframes_locked: bool,
    ) -> std::fmt::Result {
        unsafe fn fmt_helper<W: core::fmt::Write>(
            mut f: &mut W,
            frame: &Frame,
            is_last: bool,
            prefix: &str,
            subframes_locked: bool,
        ) -> core::fmt::Result {
            let location = frame.location();
            let current;
            let next;

            if is_last {
                current = format!("{prefix}└╼ {location}");
                next = format!("{prefix}   ");
            } else {
                current = format!("{prefix}├╼ {location}");
                next = format!("{prefix}");
            }

            // print all but the first three codepoints of current
            write!(&mut f, "{}", {
                let mut current = current.chars();
                current.next().unwrap();
                current.next().unwrap();
                current.next().unwrap();
                &current.as_str()
            })?;

            if subframes_locked {
                frame.subframes().for_each(|frame| {
                    writeln!(&mut f).unwrap();
                    let is_last = frame.next_frame().is_none();
                    fmt_helper(f, frame, is_last, &next, true).unwrap();
                });
            } else {
                writeln!(&mut f, "{prefix}└┈ [POLLING]")?;
            }

            Ok(())
        }

        fmt_helper(w, self, true, "  ", subframes_locked)
    }
}

impl Frame {
    /// Produces the parent frame of this frame.
    pub(crate) fn parent(&self) -> Option<&Frame> {
        if self.is_uninitialized() {
            None
        } else if let Kind::Node { parent } = self.kind {
            Some(unsafe { parent.as_ref() })
        } else {
            None
        }
    }

    /// Produces the root frame of this futures tree.
    pub(crate) fn root(&self) -> &Frame {
        let mut frame = self;
        while let Some(parent) = frame.parent() {
            frame = parent;
        }
        frame
    }

    /// Produces an iterator over this frame's ancestors.
    pub fn backtrace(&self) -> impl Iterator<Item = &Frame> + FusedIterator {
        /// An iterator that traverses up the tree of [`Frame`]s from a leaf.
        #[derive(Clone)]
        pub(crate) struct Backtrace<'a> {
            frame: Option<&'a Frame>,
        }

        impl<'a> Backtrace<'a> {
            pub(crate) fn from_leaf(frame: &'a Frame) -> Self {
                Self { frame: Some(frame) }
            }
        }

        impl<'a> Iterator for Backtrace<'a> {
            type Item = &'a Frame;

            fn next(&mut self) -> Option<Self::Item> {
                let curr = self.frame;
                self.frame = curr.and_then(Frame::parent);
                curr
            }
        }

        impl<'a> FusedIterator for Backtrace<'a> {}

        Backtrace::from_leaf(self)
    }

    /// Produces an iterator over this frame's less-recently in
    pub(crate) fn subframes(&self) -> impl Iterator<Item = &Frame> + FusedIterator {
        pub(crate) struct Subframes<'a> {
            iter: linked_list::Iter<'a, Frame>,
        }

        impl<'a> Subframes<'a> {
            pub(crate) fn from_parent(frame: &'a Frame) -> Self {
                Self {
                    iter: frame.children.with(|children| unsafe { &*children }.iter()),
                }
            }
        }

        impl<'a> Iterator for Subframes<'a> {
            type Item = &'a Frame;

            fn next(&mut self) -> Option<Self::Item> {
                self.iter.next().map(|frame| unsafe { frame.as_ref() })
            }
        }

        impl<'a> FusedIterator for Subframes<'a> {}

        Subframes::from_parent(self)
    }

    /// Produces this frame's previous (more-recently initialized) sibling (if
    /// any).
    pub fn prev_frame(&self) -> Option<&Frame> {
        unsafe {
            <Frame as linked_list::Link>::pointers(NonNull::from(self))
                .as_ref()
                .get_prev()
                .as_ref()
                .map(|f| f.as_ref())
        }
    }

    /// Produces this frame's previous (less-recently initialized) sibling (if
    /// any).
    pub fn next_frame(&self) -> Option<&Frame> {
        unsafe {
            <Frame as linked_list::Link>::pointers(NonNull::from(self))
                .as_ref()
                .get_next()
                .as_ref()
                .map(|f| f.as_ref())
        }
    }
}

impl Kind {
    /// Produces a new [`Kind::Root`].
    fn root() -> Self {
        Kind::Root {
            mutex: Mutex::new(()),
        }
    }

    /// Produces a new [`Kind::Node`].
    fn node(parent: &Frame) -> Self {
        Kind::Node {
            parent: NonNull::from(parent),
        }
    }

    /// True if kind is [`Kind::Uninitialized`].
    fn is_uninitialized(&self) -> bool {
        matches!(&self, Kind::Uninitialized)
    }
}

unsafe impl linked_list::Link for Frame {
    type Handle = NonNull<Self>;
    type Target = Self;

    fn as_raw(handle: &NonNull<Self>) -> NonNull<Self> {
        *handle
    }

    unsafe fn from_raw(ptr: NonNull<Self>) -> NonNull<Self> {
        ptr
    }

    unsafe fn pointers(target: NonNull<Self>) -> NonNull<linked_list::Pointers<Self>> {
        let me = target.as_ptr();
        let field = ::std::ptr::addr_of_mut!((*me).siblings);
        ::core::ptr::NonNull::new_unchecked(field)
    }
}