//! \[Experimental\] Deadlock detection //! //! This feature is optional and can be enabled via the `deadlock_detection` feature flag. //! //! # 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(test)] #[cfg(feature = "deadlock_detection")] mod tests { use crate::{Mutex, ReentrantMutex, RwLock}; use std::sync::{Arc, Barrier}; use std::thread::{self, sleep}; use std::time::Duration; // We need to serialize these tests since deadlock detection uses global state static DEADLOCK_DETECTION_LOCK: Mutex<()> = crate::const_mutex(()); fn check_deadlock() -> bool { use parking_lot_core::deadlock::check_deadlock; !check_deadlock().is_empty() } #[test] fn test_mutex_deadlock() { let _guard = DEADLOCK_DETECTION_LOCK.lock(); let m1: Arc> = Default::default(); let m2: Arc> = Default::default(); let m3: Arc> = 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 _guard = DEADLOCK_DETECTION_LOCK.lock(); let m1: Arc> = 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 _guard = DEADLOCK_DETECTION_LOCK.lock(); let m1: Arc> = Default::default(); let m2: Arc> = Default::default(); let m3: Arc> = 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 _guard = DEADLOCK_DETECTION_LOCK.lock(); let m1: Arc> = Default::default(); let m2: Arc> = Default::default(); let m3: Arc> = 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()); } #[cfg(rwlock_deadlock_detection_not_supported)] #[test] fn test_rwlock_deadlock_reentrant() { let _guard = DEADLOCK_DETECTION_LOCK.lock(); let m1: Arc> = 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()); } }