1//! This example demonstrates tokio's experimental task dumping functionality.
2//! This application deadlocks. Input CTRL+C to display traces of each task, or
3//! input CTRL+C twice within 1 second to quit.
4
5#[cfg(all(
6 tokio_unstable,
7 tokio_taskdump,
8 target_os = "linux",
9 any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
10))]
11#[tokio::main]
12async fn main() -> Result<(), Box<dyn std::error::Error>> {
13 use std::sync::Arc;
14 use tokio::sync::Barrier;
15
16 #[inline(never)]
17 async fn a(barrier: Arc<Barrier>) {
18 b(barrier).await
19 }
20
21 #[inline(never)]
22 async fn b(barrier: Arc<Barrier>) {
23 c(barrier).await
24 }
25
26 #[inline(never)]
27 async fn c(barrier: Arc<Barrier>) {
28 barrier.wait().await;
29 }
30
31 // Prints a task dump upon receipt of CTRL+C, or returns if CTRL+C is
32 // inputted twice within a second.
33 async fn dump_or_quit() {
34 use tokio::time::{timeout, Duration, Instant};
35 let handle = tokio::runtime::Handle::current();
36 let mut last_signal: Option<Instant> = None;
37 // wait for CTRL+C
38 while let Ok(_) = tokio::signal::ctrl_c().await {
39 // exit if a CTRL+C is inputted twice within 1 second
40 if let Some(time_since_last_signal) = last_signal.map(|i| i.elapsed()) {
41 if time_since_last_signal < Duration::from_secs(1) {
42 return;
43 }
44 }
45 last_signal = Some(Instant::now());
46
47 // capture a dump, and print each trace
48 println!("{:-<80}", "");
49 if let Ok(dump) = timeout(Duration::from_secs(2), handle.dump()).await {
50 for (i, task) in dump.tasks().iter().enumerate() {
51 let trace = task.trace();
52 println!("TASK {i}:");
53 println!("{trace}\n");
54 }
55 } else {
56 println!("Task dumping timed out. Use a native debugger (like gdb) to debug the deadlock.");
57 }
58 println!("{:-<80}", "");
59 println!("Input CTRL+C twice within 1 second to exit.");
60 }
61 }
62
63 println!("This program has a deadlock.");
64 println!("Input CTRL+C to print a task dump.");
65 println!("Input CTRL+C twice within 1 second to exit.");
66
67 // oops! this barrier waits for one more task than will ever come.
68 let barrier = Arc::new(Barrier::new(3));
69
70 let task_1 = tokio::spawn(a(barrier.clone()));
71 let task_2 = tokio::spawn(a(barrier));
72
73 tokio::select!(
74 _ = dump_or_quit() => {},
75 _ = task_1 => {},
76 _ = task_2 => {},
77 );
78
79 Ok(())
80}
81
82#[cfg(not(all(
83 tokio_unstable,
84 tokio_taskdump,
85 target_os = "linux",
86 any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
87)))]
88fn main() {
89 println!("task dumps are not available")
90}
91