blob: d043a82dcddb0427ad46e23fff9d3012cf45f8af [file] [log] [blame]
//! [Experimental] Deadlock detection
//!
//! This feature is optional and can be enabled via the `deadlock_detection` feature flag.
//!
//! Enabling this feature will *remove* the `Send` marker from `Mutex` and `RwLock` Guards
//! as locking/unlocking in different threads is incompatible with the deadlock detector.
//!
//! # Example
//!
//! ```
//! #[cfg(feature = "deadlock_detection")]
//! { // only for #[cfg]
//! use std::thread;
//! use std::time::Duration;
//! use parking_lot::deadlock;
//!
//! // Create a background thread which checks for deadlocks every 10s
//! thread::spawn(move || {
//! loop {
//! thread::sleep(Duration::from_secs(10));
//! let deadlocks = deadlock::check_deadlock();
//! if deadlocks.is_empty() {
//! continue;
//! }
//!
//! println!("{} deadlocks detected", deadlocks.len());
//! for (i, threads) in deadlocks.iter().enumerate() {
//! println!("Deadlock #{}", i);
//! for t in threads {
//! println!("Thread Id {:#?}", t.thread_id());
//! println!("{:#?}", t.backtrace());
//! }
//! }
//! }
//! });
//! } // only for #[cfg]
//! ```
#[cfg(feature = "deadlock_detection")]
pub use parking_lot_core::deadlock::check_deadlock;
pub(crate) use parking_lot_core::deadlock::{acquire_resource, release_resource};
#[cfg(not(feature = "deadlock_detection"))]
pub(crate) struct DeadlockDetectionMarker;
// when deadlock detector is enabled we want the marker to be !Send + Sync
#[cfg(feature = "deadlock_detection")]
use std::marker::PhantomData;
#[cfg(feature = "deadlock_detection")]
pub(crate) struct DeadlockDetectionMarker(PhantomData<*mut ()>); // !Send
#[cfg(feature = "deadlock_detection")]
unsafe impl Sync for DeadlockDetectionMarker {} // Sync
#[cfg(test)]
#[cfg(feature = "deadlock_detection")]
mod tests {
use std::thread::{self, sleep};
use std::sync::{Arc, Barrier};
use std::time::Duration;
use {Mutex, RwLock, ReentrantMutex};
fn check_deadlock() -> bool {
use parking_lot_core::deadlock::check_deadlock;
!check_deadlock().is_empty()
}
#[test]
fn test_mutex_deadlock() {
let m1: Arc<Mutex<()>> = Default::default();
let m2: Arc<Mutex<()>> = Default::default();
let m3: Arc<Mutex<()>> = Default::default();
let b = Arc::new(Barrier::new(4));
let m1_ = m1.clone();
let m2_ = m2.clone();
let m3_ = m3.clone();
let b1 = b.clone();
let b2 = b.clone();
let b3 = b.clone();
assert!(!check_deadlock());
let _t1 = thread::spawn(move || {
let _g = m1.lock();
b1.wait();
let _ = m2_.lock();
});
let _t2 = thread::spawn(move || {
let _g = m2.lock();
b2.wait();
let _ = m3_.lock();
});
let _t3 = thread::spawn(move || {
let _g = m3.lock();
b3.wait();
let _ = m1_.lock();
});
assert!(!check_deadlock());
b.wait();
sleep(Duration::from_millis(50));
assert!(check_deadlock());
assert!(!check_deadlock());
}
#[test]
fn test_mutex_deadlock_reentrant() {
let m1: Arc<Mutex<()>> = Default::default();
assert!(!check_deadlock());
let _t1 = thread::spawn(move || {
let _g = m1.lock();
let _ = m1.lock();
});
sleep(Duration::from_millis(50));
assert!(check_deadlock());
assert!(!check_deadlock());
}
#[test]
fn test_remutex_deadlock() {
let m1: Arc<ReentrantMutex<()>> = Default::default();
let m2: Arc<ReentrantMutex<()>> = Default::default();
let m3: Arc<ReentrantMutex<()>> = Default::default();
let b = Arc::new(Barrier::new(4));
let m1_ = m1.clone();
let m2_ = m2.clone();
let m3_ = m3.clone();
let b1 = b.clone();
let b2 = b.clone();
let b3 = b.clone();
assert!(!check_deadlock());
let _t1 = thread::spawn(move || {
let _g = m1.lock();
let _g = m1.lock();
b1.wait();
let _ = m2_.lock();
});
let _t2 = thread::spawn(move || {
let _g = m2.lock();
let _g = m2.lock();
b2.wait();
let _ = m3_.lock();
});
let _t3 = thread::spawn(move || {
let _g = m3.lock();
let _g = m3.lock();
b3.wait();
let _ = m1_.lock();
});
assert!(!check_deadlock());
b.wait();
sleep(Duration::from_millis(50));
assert!(check_deadlock());
assert!(!check_deadlock());
}
#[test]
fn test_rwlock_deadlock() {
let m1: Arc<RwLock<()>> = Default::default();
let m2: Arc<RwLock<()>> = Default::default();
let m3: Arc<RwLock<()>> = Default::default();
let b = Arc::new(Barrier::new(4));
let m1_ = m1.clone();
let m2_ = m2.clone();
let m3_ = m3.clone();
let b1 = b.clone();
let b2 = b.clone();
let b3 = b.clone();
assert!(!check_deadlock());
let _t1 = thread::spawn(move || {
let _g = m1.read();
b1.wait();
let _g = m2_.write();
});
let _t2 = thread::spawn(move || {
let _g = m2.read();
b2.wait();
let _g = m3_.write();
});
let _t3 = thread::spawn(move || {
let _g = m3.read();
b3.wait();
let _ = m1_.write();
});
assert!(!check_deadlock());
b.wait();
sleep(Duration::from_millis(50));
assert!(check_deadlock());
assert!(!check_deadlock());
}
#[test]
fn test_rwlock_deadlock_reentrant() {
let m1: Arc<RwLock<()>> = Default::default();
assert!(!check_deadlock());
let _t1 = thread::spawn(move || {
let _g = m1.read();
let _ = m1.write();
});
sleep(Duration::from_millis(50));
assert!(check_deadlock());
assert!(!check_deadlock());
}
}