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
//! [`tokio-console`] instrumentation.
//!
//! [`tokio-console`] collects its data through [`tracing`] spans and events
//! that follow a fixed naming convention. It is *not* tied to tokio's internals
//! in any way, so any executor emitting the same spans and events can be
//! observed with it.
//!
//! Enable the `console` feature to make this executor emit them:
//!
//! * every task gets a `runtime.spawn` span, entered while the task is polled,
//! so that the console can compute poll counts, busy/idle/scheduled times and
//! the poll time histogram;
//! * every waker operation emits a `runtime::waker` event, so that the console
//! can compute waker counts and detect self-wakes and lost wakers;
//! * a closure handed to the blocking pool gets such a span too, entered around
//! the closure instead of around a poll, so that the time spent in it is
//! reported as busy time rather than as idle time.
//!
//! When the feature is disabled, all of this compiles down to nothing: the
//! types in this module become zero-sized and every method an empty inlined
//! function.
//!
//! # Usage
//!
//! `console-subscriber` refuses to run unless it can prove that the runtime is
//! instrumented, which for tokio means the `tokio_unstable` cfg. For other
//! runtimes it provides the `console_without_tokio_unstable` escape hatch, so a
//! binary observing compio needs:
//!
//! ```toml
//! # .cargo/config.toml
//! [build]
//! rustflags = ["--cfg", "console_without_tokio_unstable"]
//! ```
//!
//! Depending on `console-subscriber` and installing it is then all it takes:
//!
//! ```ignore
//! console_subscriber::init();
//! compio::runtime::Runtime::new().unwrap().block_on(async {
//! // ...
//! });
//! ```
//!
//! # Limitations
//!
//! * The console's data model has one runtime per process, while compio is
//! thread-per-core and has one executor per thread. The tasks of all of them
//! are listed together; the `thread` field tells them apart.
//! * The subscriber has to be the global default, which
//! `console_subscriber::init` makes it. A span carries the subscriber it was
//! created with, but an event goes to whichever one is current on the thread
//! emitting it, so a thread-local subscriber misses the waker operations
//! other threads perform. Wakers cross threads routinely — that is what
//! waking a task from another executor is — and the clone and drop counts of
//! one that does no longer balance, leaving the console to report a lost
//! waker that is not lost.
//! * A `block_on` nested inside a task — a runtime built within another one —
//! reports the two as separate tasks, but both of their spans are entered on
//! the same stack. The console attributes the polls to the inner one for as
//! long as that is the case.
//! * A blocking task has no waker operations, since it is a closure rather than
//! a future. The console knows this from its `kind` and does not report a
//! lost waker for it.
//! * A task spawned by an `async fn` is attributed to that function rather than
//! to its caller, since [`#[track_caller]`][async-track-caller] is a no-op on
//! `async fn`s and [`SpawnMeta`] therefore cannot be forwarded through them.
//! A function that wants the caller instead can be a plain `fn` returning a
//! future, capturing the [`SpawnMeta`] before the `async` block it returns —
//! at the cost of an opaque return type, and of running whatever precedes the
//! block when it is called rather than when it is first polled. The ones
//! compio spawns itself are named either way.
//!
//! Nightly's `async_fn_track_caller` is not a substitute: it reports the
//! caller of `poll`, which is the `.await` when a future is awaited directly,
//! but a line inside `join!`, `select!` or whichever combinator drives it
//! otherwise.
//! * The resources tab stays empty: timers and in-flight operations are not
//! instrumented yet.
//! * A task's span is closed even when the thread is unwinding, or the console
//! would show the task as running forever. The subscriber therefore runs
//! during a panic, where a panic of its own aborts instead of unwinding.
//!
//! [`tokio-console`]: https://github.com/tokio-rs/console
//! [`tracing`]: https://docs.rs/tracing
//! [async-track-caller]: https://github.com/rust-lang/rust/issues/110011
cfg_select!
pub use TaskSpan;
pub use ;
/// An operation on a task's waker, reported as a `runtime::waker` event.
///
/// Note that [`Waker::wake`](std::task::Waker::wake) does not call the `drop`
/// implementation, so the console counts [`Self::Wake`] as both a wake and a
/// drop. Emitting an additional [`Self::Drop`] for it would make the live waker
/// count (clones - drops) go negative.
pub
/// Assertions that the two variants above present the same surface.
///
/// Only one of them is ever compiled, and the one compiled by default is the
/// one nearly every build uses: a difference between the two shows up as a
/// build failure for whoever turns the feature on, long after the code that
/// assumed the other shape was written.
///
/// Coercing each item to a function pointer pins its whole signature, and
/// naming [`EnterGuard`] with a lifetime pins the shape of the guard: the
/// enabled one borrows the span, so a disabled one that owns itself, and would
/// let code outlive the span it is timing, does not have a lifetime to name.