use deloxide::{Mutex, thread};
use rand::Rng;
use std::{
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
time::Duration,
};
mod common;
use common::{expect_deadlock, start_detector};
#[test]
fn test_random_ring_deadlock() {
let harness = start_detector();
let mut rng = rand::rng();
let n = rng.random_range(3..=8);
println!("→ testing a ring of {} threads", n);
let locks: Vec<_> = (0..n)
.map(|i| Arc::new(Mutex::new(format!("L{}", i))))
.collect();
let ready_count = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::with_capacity(n);
for i in 0..n {
let first = locks[i].clone();
let second = locks[(i + 1) % n].clone();
let ready = ready_count.clone();
handles.push(thread::spawn(move || {
let mut rng = rand::rng();
ready.fetch_add(1, Ordering::SeqCst);
while ready.load(Ordering::SeqCst) < n {
thread::yield_now();
}
thread::sleep(Duration::from_millis(rng.random_range(0..50)));
let _a = first.lock();
thread::sleep(Duration::from_millis(rng.random_range(50..100)));
let _b = second.lock();
thread::sleep(Duration::from_millis(200));
}));
}
let info = expect_deadlock(&harness, Duration::from_secs(5));
assert_eq!(
info.thread_cycle.len(),
n,
"Expected a cycle of length {}, got {:?}",
n,
info.thread_cycle
);
assert!(
!info.thread_waiting_for_locks.is_empty(),
"No waiting relationships recorded"
);
println!("✔ detected {}-cycle deadlock: {:?}", n, info.thread_cycle);
}