use async_inspect::deadlock::{DeadlockDetector, ResourceInfo, ResourceKind};
use async_inspect::task::TaskId;
use colored::Colorize;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
async fn deadlock_scenario_1() {
println!(
"{}",
"=== Scenario 1: Classic Mutex Deadlock ==="
.bright_yellow()
.bold()
);
println!();
let detector = DeadlockDetector::new();
let mutex_a = Arc::new(Mutex::new(0));
let mutex_b = Arc::new(Mutex::new(0));
let res_a = ResourceInfo::new(ResourceKind::Mutex, "mutex_a".to_string())
.with_address(Arc::as_ptr(&mutex_a) as usize);
let res_b = ResourceInfo::new(ResourceKind::Mutex, "mutex_b".to_string())
.with_address(Arc::as_ptr(&mutex_b) as usize);
let res_a_id = res_a.id;
let res_b_id = res_b.id;
let _ = detector.register_resource(res_a);
let _ = detector.register_resource(res_b);
let task1_id = TaskId::new();
let detector_clone1 = detector.clone();
let mutex_a_clone1 = Arc::clone(&mutex_a);
let mutex_b_clone1 = Arc::clone(&mutex_b);
let task1 = tokio::spawn(async move {
println!("Task 1: Acquiring mutex_a...");
let _guard_a = mutex_a_clone1.lock().await;
detector_clone1.acquire(task1_id, res_a_id);
println!("Task 1: Acquired mutex_a");
tokio::time::sleep(Duration::from_millis(50)).await;
println!("Task 1: Waiting for mutex_b...");
detector_clone1.wait_for(task1_id, res_b_id);
let _guard_b = mutex_b_clone1.lock().await;
println!("Task 1: Acquired mutex_b (should never print)");
});
let task2_id = TaskId::new();
let detector_clone2 = detector.clone();
let mutex_a_clone2 = Arc::clone(&mutex_a);
let mutex_b_clone2 = Arc::clone(&mutex_b);
let task2 = tokio::spawn(async move {
println!("Task 2: Acquiring mutex_b...");
let _guard_b = mutex_b_clone2.lock().await;
detector_clone2.acquire(task2_id, res_b_id);
println!("Task 2: Acquired mutex_b");
tokio::time::sleep(Duration::from_millis(50)).await;
println!("Task 2: Waiting for mutex_a...");
detector_clone2.wait_for(task2_id, res_a_id);
let _guard_a = mutex_a_clone2.lock().await;
println!("Task 2: Acquired mutex_a (should never print)");
});
tokio::time::sleep(Duration::from_millis(200)).await;
println!("\n{}\n", "[*] Checking for deadlocks...".bright_cyan());
let deadlocks = detector.detect_deadlocks();
if deadlocks.is_empty() {
println!("{}", "[OK] No deadlocks detected".on_green().white().bold());
} else {
for (i, cycle) in deadlocks.iter().enumerate() {
println!(
"{}",
format!("[FAIL] Deadlock #{} detected!", i + 1)
.on_red()
.white()
.bold()
);
println!("{}", cycle.describe());
println!();
println!("Resources involved:");
for &res_id in &cycle.resources {
if let Some(resource) = detector.get_resource(res_id) {
println!(" - {}", resource);
}
}
println!();
}
println!("{}", "[*] Suggestions:".on_yellow().white().bold());
println!(
" {} {}",
"•".bright_yellow(),
"Acquire locks in consistent order (always A before B)".bright_white()
);
println!(
" {} {}",
"•".bright_yellow(),
"Use try_lock() with timeout".bright_white()
);
println!(
" {} {}",
"•".bright_yellow(),
"Consider lock-free data structures".bright_white()
);
}
task1.abort();
task2.abort();
println!();
}
async fn no_deadlock_scenario() {
println!(
"{}",
"=== Scenario 2: Proper Lock Ordering (No Deadlock) ==="
.bright_yellow()
.bold()
);
println!();
let detector = DeadlockDetector::new();
let mutex_a = Arc::new(Mutex::new(0));
let mutex_b = Arc::new(Mutex::new(0));
let res_a = ResourceInfo::new(ResourceKind::Mutex, "mutex_a".to_string());
let res_b = ResourceInfo::new(ResourceKind::Mutex, "mutex_b".to_string());
let res_a_id = res_a.id;
let res_b_id = res_b.id;
let _ = detector.register_resource(res_a);
let _ = detector.register_resource(res_b);
let tasks: Vec<_> = (1..=3)
.map(|i| {
let detector_clone = detector.clone();
let mutex_a_clone = Arc::clone(&mutex_a);
let mutex_b_clone = Arc::clone(&mutex_b);
tokio::spawn(async move {
let task_id = TaskId::new();
println!("Task {}: Acquiring mutex_a...", i);
let _guard_a = mutex_a_clone.lock().await;
detector_clone.acquire(task_id, res_a_id);
println!("Task {}: Acquired mutex_a", i);
tokio::time::sleep(Duration::from_millis(10)).await;
println!("Task {}: Acquiring mutex_b...", i);
let _guard_b = mutex_b_clone.lock().await;
detector_clone.acquire(task_id, res_b_id);
println!("Task {}: Acquired mutex_b", i);
tokio::time::sleep(Duration::from_millis(10)).await;
detector_clone.release(task_id, res_b_id);
detector_clone.release(task_id, res_a_id);
println!("Task {}: Released all locks", i);
})
})
.collect();
for task in tasks {
let _ = task.await;
}
println!("\n{}\n", "[*] Checking for deadlocks...".bright_cyan());
let deadlocks = detector.detect_deadlocks();
if deadlocks.is_empty() {
println!(
"{}",
"[OK] No deadlocks detected - all tasks completed successfully!"
.on_green()
.white()
.bold()
);
} else {
println!(
"{}",
"[FAIL] Unexpected deadlock detected!"
.on_red()
.white()
.bold()
);
}
println!();
}
#[tokio::main]
async fn main() {
println!("{}", "[async-inspect]".on_purple().white().bold());
println!("{}", "Deadlock Detection Example".bright_blue());
println!("===============================================\n");
deadlock_scenario_1().await;
no_deadlock_scenario().await;
println!("{}", "=== Summary ===".bright_yellow().bold());
println!();
println!(
"{} {}",
"[async-inspect]".on_purple().white().bold(),
"successfully:".bright_white().bold()
);
println!(
" {} {}",
"[OK]".on_green().white().bold(),
"Detected circular dependencies".bright_white()
);
println!(
" {} {}",
"[OK]".on_green().white().bold(),
"Identified involved tasks and resources".bright_white()
);
println!(
" {} {}",
"[OK]".on_green().white().bold(),
"Generated actionable suggestions".bright_white()
);
println!();
println!(
"{}",
"Deadlock detection helps you find and fix".bright_cyan()
);
println!(
"{}",
"circular wait conditions before they reach production!".bright_cyan()
);
}