Skip to content

Commit

Permalink
Merge pull request #36 from bertptrs/example/show-potential
Browse files Browse the repository at this point in the history
  • Loading branch information
bertptrs authored Nov 13, 2023
2 parents 6199598 + 74b4fe0 commit 9ca5af2
Showing 1 changed file with 53 additions and 17 deletions.
70 changes: 53 additions & 17 deletions examples/mutex_cycle.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,62 @@
//! Show what a crash looks like
//!
//! This shows what a traceback of a cycle detection looks like. It is expected to crash.
//! This shows what a traceback of a cycle detection looks like. It is expected to crash when run in
//! debug mode, because it might deadlock. In release mode, no tracing is used and the program may
//! do any of the following:
//!
//! - Return a random valuation of `a`, `b`, and `c`. The implementation has a race-condition by
//! design. I have observed (4, 3, 6), but also (6, 3, 5).
//! - Deadlock forever.
//!
//! One can increase the SLEEP_TIME constant to increase the likelihood of a deadlock to occur. On
//! my machine, 1ns of sleep time gives about a 50/50 chance of the program deadlocking.
use std::thread;
use std::time::Duration;

use tracing_mutex::stdsync::Mutex;

fn main() {
let a = Mutex::new(());
let b = Mutex::new(());
let c = Mutex::new(());
let a = Mutex::new(1);
let b = Mutex::new(2);
let c = Mutex::new(3);

// Increase this time to increase the likelihood of a deadlock.
const SLEEP_TIME: Duration = Duration::from_nanos(1);

// Depending on random CPU performance, this section may deadlock, or may return a result. With
// tracing enabled, the potential deadlock is always detected and a backtrace should be
// produced.
thread::scope(|s| {
// Create an edge from a to b
s.spawn(|| {
let a = a.lock().unwrap();
thread::sleep(SLEEP_TIME);
*b.lock().unwrap() += *a;
});

// Create an edge from a to b
{
let _a = a.lock();
let _b = b.lock();
}
// Create an edge from b to c
s.spawn(|| {
let b = b.lock().unwrap();
thread::sleep(SLEEP_TIME);
*c.lock().unwrap() += *b;
});

// Create an edge from b to c
{
let _b = b.lock();
let _c = c.lock();
}
// Create an edge from c to a
//
// N.B. the program can crash on any of the three edges, as there is no guarantee which
// thread will execute first. Nevertheless, any one of them is guaranteed to panic with
// tracing enabled.
s.spawn(|| {
let c = c.lock().unwrap();
thread::sleep(SLEEP_TIME);
*a.lock().unwrap() += *c;
});
});

// Now crash by trying to add an edge from c to a
let _c = c.lock();
let _a = a.lock(); // This line will crash
println!(
"{}, {}, {}",
a.into_inner().unwrap(),
b.into_inner().unwrap(),
c.into_inner().unwrap()
);
}

0 comments on commit 9ca5af2

Please sign in to comment.