|
1 |
| -//! This example demonstrates tokio's experimental taskdumping functionality. |
| 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. |
2 | 4 |
|
3 | 5 | #[cfg(all(
|
4 | 6 | tokio_unstable,
|
|
7 | 9 | any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
|
8 | 10 | ))]
|
9 | 11 | #[tokio::main]
|
10 |
| -async fn main() { |
11 |
| - use std::hint::black_box; |
| 12 | +async fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 13 | + use std::sync::Arc; |
| 14 | + use tokio::sync::Barrier; |
12 | 15 |
|
13 | 16 | #[inline(never)]
|
14 |
| - async fn a() { |
15 |
| - black_box(b()).await |
| 17 | + async fn a(barrier: Arc<Barrier>) { |
| 18 | + b(barrier).await |
16 | 19 | }
|
17 | 20 |
|
18 | 21 | #[inline(never)]
|
19 |
| - async fn b() { |
20 |
| - black_box(c()).await |
| 22 | + async fn b(barrier: Arc<Barrier>) { |
| 23 | + c(barrier).await |
21 | 24 | }
|
22 | 25 |
|
23 | 26 | #[inline(never)]
|
24 |
| - async fn c() { |
25 |
| - loop { |
26 |
| - tokio::task::yield_now().await; |
27 |
| - } |
| 27 | + async fn c(barrier: Arc<Barrier>) { |
| 28 | + barrier.wait().await; |
28 | 29 | }
|
29 | 30 |
|
30 |
| - async fn dump() { |
| 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}; |
31 | 35 | let handle = tokio::runtime::Handle::current();
|
32 |
| - let dump = handle.dump().await; |
| 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()); |
33 | 46 |
|
34 |
| - for (i, task) in dump.tasks().iter().enumerate() { |
35 |
| - let trace = task.trace(); |
36 |
| - println!("task {i} trace:"); |
37 |
| - println!("{trace}\n"); |
| 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."); |
38 | 60 | }
|
39 | 61 | }
|
40 | 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 | + |
41 | 73 | tokio::select!(
|
42 |
| - biased; |
43 |
| - _ = tokio::spawn(a()) => {}, |
44 |
| - _ = tokio::spawn(b()) => {}, |
45 |
| - _ = tokio::spawn(c()) => {}, |
46 |
| - _ = dump() => {}, |
| 74 | + _ = dump_or_quit() => {}, |
| 75 | + _ = task_1 => {}, |
| 76 | + _ = task_2 => {}, |
47 | 77 | );
|
| 78 | + |
| 79 | + Ok(()) |
48 | 80 | }
|
49 | 81 |
|
50 | 82 | #[cfg(not(all(
|
|
0 commit comments