Skip to content

Commit 85553e6

Browse files
committed
taskdump: implement task dumps for multi-thread runtime
This PR implements task dumps on the multi-thread runtime. It complements tokio-rs#5608, which implemented task dumps on the current-thread runtime.
1 parent 52bc6b6 commit 85553e6

File tree

5 files changed

+275
-21
lines changed

5 files changed

+275
-21
lines changed

examples/dump.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
target_os = "linux",
77
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
88
))]
9-
#[tokio::main(flavor = "current_thread")]
9+
#[tokio::main(worker_threads = 72)]
1010
async fn main() {
11+
println!("pid={}", std::process::id());
12+
1113
use std::hint::black_box;
1214

1315
#[inline(never)]
@@ -22,21 +24,29 @@ async fn main() {
2224

2325
#[inline(never)]
2426
async fn c() {
25-
black_box(tokio::task::yield_now()).await
27+
loop {
28+
tokio::task::yield_now().await;
29+
}
2630
}
2731

28-
tokio::spawn(a());
29-
tokio::spawn(b());
30-
tokio::spawn(c());
31-
32-
let handle = tokio::runtime::Handle::current();
33-
let dump = handle.dump();
32+
async fn dump() {
33+
let handle = tokio::runtime::Handle::current();
34+
let dump = handle.dump().await;
3435

35-
for (i, task) in dump.tasks().iter().enumerate() {
36-
let trace = task.trace();
37-
println!("task {i} trace:");
38-
println!("{trace}");
36+
for (i, task) in dump.tasks().iter().enumerate() {
37+
let trace = task.trace();
38+
println!("task {i} trace:");
39+
println!("{trace}");
40+
}
3941
}
42+
43+
tokio::select!(
44+
biased;
45+
_ = tokio::spawn(a()) => {},
46+
_ = dump() => {},
47+
);
48+
49+
println!("END OF MAIN");
4050
}
4151

4252
#[cfg(not(all(

tokio/src/runtime/handle.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,20 @@ cfg_metrics! {
341341
cfg_taskdump! {
342342
impl Handle {
343343
/// Capture a snapshot of this runtime's state.
344-
pub fn dump(&self) -> crate::runtime::Dump {
344+
pub async fn dump(&self) -> crate::runtime::Dump {
345345
match &self.inner {
346346
scheduler::Handle::CurrentThread(handle) => handle.dump(),
347347
#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))]
348-
scheduler::Handle::MultiThread(_) =>
349-
unimplemented!("taskdumps are unsupported on the multi-thread runtime"),
348+
scheduler::Handle::MultiThread(handle) => {
349+
// perform the trace in a separate thread
350+
let handle = handle.clone();
351+
let (tx, rx) = crate::sync::oneshot::channel();
352+
crate::loom::thread::spawn(|| {
353+
let handle = handle;
354+
tx.send(handle.dump()).unwrap();
355+
});
356+
rx.await.unwrap()
357+
},
350358
}
351359
}
352360
}

tokio/src/runtime/scheduler/multi_thread/handle.rs

+31
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,37 @@ cfg_metrics! {
9595
}
9696
}
9797

98+
cfg_taskdump! {
99+
impl Handle {
100+
pub(crate) fn dump(&self) -> crate::runtime::Dump {
101+
let trace_status = &self.shared.trace_status;
102+
103+
println!("{:?} Handle::dump: trace requested; waiting for pending dump requests to complete", std::thread::current().id());
104+
105+
// If a dump is in progress, block.
106+
trace_status.start_trace_request(&self);
107+
108+
println!("{:?} Handle::dump: no pending dump requests; waiting for result for this request", std::thread::current().id());
109+
110+
let result = loop {
111+
if let Some(result) = trace_status.take_result() {
112+
break result;
113+
} else {
114+
self.notify_all();
115+
crate::loom::thread::yield_now();
116+
}
117+
};
118+
119+
println!("{:?} Handle::dump: result received; allowing other dump requests to proceed", std::thread::current().id());
120+
121+
// Allow other queued dumps to proceed.
122+
trace_status.end_trace_request(&self);
123+
124+
result
125+
}
126+
}
127+
}
128+
98129
impl fmt::Debug for Handle {
99130
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
100131
fmt.debug_struct("multi_thread::Handle { ... }").finish()

0 commit comments

Comments
 (0)