use deloxide::{Mutex, thread};
use std::sync::Arc;
use std::time::Duration;
mod common;
use common::{DEADLOCK_TIMEOUT, expect_deadlock, start_detector};
#[test]
fn test_five_lock_cycle_deadlock() {
let harness = start_detector();
let a = Arc::new(Mutex::new(()));
let b = Arc::new(Mutex::new(()));
let c = Arc::new(Mutex::new(()));
let d = Arc::new(Mutex::new(()));
let e = Arc::new(Mutex::new(()));
let mut handles = vec![];
{
let (l0, l1, l2, l3, l4) = (a.clone(), b.clone(), c.clone(), d.clone(), e.clone());
handles.push(thread::spawn(move || {
let _g0 = l0.lock();
thread::sleep(Duration::from_millis(50));
let _g1 = l1.lock();
thread::sleep(Duration::from_millis(50));
let _g2 = l2.lock();
thread::sleep(Duration::from_millis(50));
let _g3 = l3.lock();
thread::sleep(Duration::from_millis(50));
let _g4 = l4.lock();
}));
}
{
let (l0, l1, l2, l3, l4) = (b.clone(), c.clone(), d.clone(), e.clone(), a.clone());
handles.push(thread::spawn(move || {
thread::sleep(Duration::from_millis(100)); let _g0 = l0.lock();
thread::sleep(Duration::from_millis(50));
let _g1 = l1.lock();
thread::sleep(Duration::from_millis(50));
let _g2 = l2.lock();
thread::sleep(Duration::from_millis(50));
let _g3 = l3.lock();
thread::sleep(Duration::from_millis(50));
let _g4 = l4.lock();
}));
}
{
let (l0, l1, l2, l3, l4) = (c.clone(), d.clone(), e.clone(), a.clone(), b.clone());
handles.push(thread::spawn(move || {
thread::sleep(Duration::from_millis(200)); let _g0 = l0.lock();
thread::sleep(Duration::from_millis(50));
let _g1 = l1.lock();
thread::sleep(Duration::from_millis(50));
let _g2 = l2.lock();
thread::sleep(Duration::from_millis(50));
let _g3 = l3.lock();
thread::sleep(Duration::from_millis(50));
let _g4 = l4.lock();
}));
}
{
let (l0, l1, l2, l3, l4) = (d.clone(), e.clone(), a.clone(), b.clone(), c.clone());
handles.push(thread::spawn(move || {
thread::sleep(Duration::from_millis(300)); let _g0 = l0.lock();
thread::sleep(Duration::from_millis(50));
let _g1 = l1.lock();
thread::sleep(Duration::from_millis(50));
let _g2 = l2.lock();
thread::sleep(Duration::from_millis(50));
let _g3 = l3.lock();
thread::sleep(Duration::from_millis(50));
let _g4 = l4.lock();
}));
}
{
let (l0, l1, l2, l3, l4) = (e.clone(), a.clone(), b.clone(), c.clone(), d.clone());
handles.push(thread::spawn(move || {
thread::sleep(Duration::from_millis(400)); let _g0 = l0.lock();
thread::sleep(Duration::from_millis(50));
let _g1 = l1.lock();
thread::sleep(Duration::from_millis(50));
let _g2 = l2.lock();
thread::sleep(Duration::from_millis(50));
let _g3 = l3.lock();
thread::sleep(Duration::from_millis(50));
let _g4 = l4.lock();
}));
}
let info = expect_deadlock(&harness, DEADLOCK_TIMEOUT);
assert!(
info.thread_cycle.len() >= 1,
"Deadlock should involve at least 1 thread"
);
assert!(
!info.thread_waiting_for_locks.is_empty() || info.lock_order_cycle.is_some(),
"Should have either thread waiting relationships or lock order cycle"
);
println!("Test complete - threads are intentionally left running in a deadlock.");
}