summaryrefslogtreecommitdiffstats
path: root/third_party/rust/parking_lot/src/deadlock.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/parking_lot/src/deadlock.rs')
-rw-r--r--third_party/rust/parking_lot/src/deadlock.rs232
1 files changed, 232 insertions, 0 deletions
diff --git a/third_party/rust/parking_lot/src/deadlock.rs b/third_party/rust/parking_lot/src/deadlock.rs
new file mode 100644
index 0000000000..0fab7228c9
--- /dev/null
+++ b/third_party/rust/parking_lot/src/deadlock.rs
@@ -0,0 +1,232 @@
+//! \[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<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 _guard = DEADLOCK_DETECTION_LOCK.lock();
+
+ 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 _guard = DEADLOCK_DETECTION_LOCK.lock();
+
+ 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 _guard = DEADLOCK_DETECTION_LOCK.lock();
+
+ 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());
+ }
+
+ #[cfg(rwlock_deadlock_detection_not_supported)]
+ #[test]
+ fn test_rwlock_deadlock_reentrant() {
+ let _guard = DEADLOCK_DETECTION_LOCK.lock();
+
+ 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());
+ }
+}