1 | //! \[Experimental\] Deadlock detection |
2 | //! |
3 | //! This feature is optional and can be enabled via the `deadlock_detection` feature flag. |
4 | //! |
5 | //! # Example |
6 | //! |
7 | //! ``` |
8 | //! #[cfg(feature = "deadlock_detection" )] |
9 | //! { // only for #[cfg] |
10 | //! use std::thread; |
11 | //! use std::time::Duration; |
12 | //! use parking_lot::deadlock; |
13 | //! |
14 | //! // Create a background thread which checks for deadlocks every 10s |
15 | //! thread::spawn(move || { |
16 | //! loop { |
17 | //! thread::sleep(Duration::from_secs(10)); |
18 | //! let deadlocks = deadlock::check_deadlock(); |
19 | //! if deadlocks.is_empty() { |
20 | //! continue; |
21 | //! } |
22 | //! |
23 | //! println!("{} deadlocks detected" , deadlocks.len()); |
24 | //! for (i, threads) in deadlocks.iter().enumerate() { |
25 | //! println!("Deadlock #{}" , i); |
26 | //! for t in threads { |
27 | //! println!("Thread Id {:#?}" , t.thread_id()); |
28 | //! println!("{:#?}" , t.backtrace()); |
29 | //! } |
30 | //! } |
31 | //! } |
32 | //! }); |
33 | //! } // only for #[cfg] |
34 | //! ``` |
35 | |
36 | #[cfg (feature = "deadlock_detection" )] |
37 | pub use parking_lot_core::deadlock::check_deadlock; |
38 | pub(crate) use parking_lot_core::deadlock::{acquire_resource, release_resource}; |
39 | |
40 | #[cfg (test)] |
41 | #[cfg (feature = "deadlock_detection" )] |
42 | mod tests { |
43 | use crate::{Mutex, ReentrantMutex, RwLock}; |
44 | use std::sync::{Arc, Barrier}; |
45 | use std::thread::{self, sleep}; |
46 | use std::time::Duration; |
47 | |
48 | // We need to serialize these tests since deadlock detection uses global state |
49 | static DEADLOCK_DETECTION_LOCK: Mutex<()> = crate::const_mutex(()); |
50 | |
51 | fn check_deadlock() -> bool { |
52 | use parking_lot_core::deadlock::check_deadlock; |
53 | !check_deadlock().is_empty() |
54 | } |
55 | |
56 | #[test] |
57 | fn test_mutex_deadlock() { |
58 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
59 | |
60 | let m1: Arc<Mutex<()>> = Default::default(); |
61 | let m2: Arc<Mutex<()>> = Default::default(); |
62 | let m3: Arc<Mutex<()>> = Default::default(); |
63 | let b = Arc::new(Barrier::new(4)); |
64 | |
65 | let m1_ = m1.clone(); |
66 | let m2_ = m2.clone(); |
67 | let m3_ = m3.clone(); |
68 | let b1 = b.clone(); |
69 | let b2 = b.clone(); |
70 | let b3 = b.clone(); |
71 | |
72 | assert!(!check_deadlock()); |
73 | |
74 | let _t1 = thread::spawn(move || { |
75 | let _g = m1.lock(); |
76 | b1.wait(); |
77 | let _ = m2_.lock(); |
78 | }); |
79 | |
80 | let _t2 = thread::spawn(move || { |
81 | let _g = m2.lock(); |
82 | b2.wait(); |
83 | let _ = m3_.lock(); |
84 | }); |
85 | |
86 | let _t3 = thread::spawn(move || { |
87 | let _g = m3.lock(); |
88 | b3.wait(); |
89 | let _ = m1_.lock(); |
90 | }); |
91 | |
92 | assert!(!check_deadlock()); |
93 | |
94 | b.wait(); |
95 | sleep(Duration::from_millis(50)); |
96 | assert!(check_deadlock()); |
97 | |
98 | assert!(!check_deadlock()); |
99 | } |
100 | |
101 | #[test] |
102 | fn test_mutex_deadlock_reentrant() { |
103 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
104 | |
105 | let m1: Arc<Mutex<()>> = Default::default(); |
106 | |
107 | assert!(!check_deadlock()); |
108 | |
109 | let _t1 = thread::spawn(move || { |
110 | let _g = m1.lock(); |
111 | let _ = m1.lock(); |
112 | }); |
113 | |
114 | sleep(Duration::from_millis(50)); |
115 | assert!(check_deadlock()); |
116 | |
117 | assert!(!check_deadlock()); |
118 | } |
119 | |
120 | #[test] |
121 | fn test_remutex_deadlock() { |
122 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
123 | |
124 | let m1: Arc<ReentrantMutex<()>> = Default::default(); |
125 | let m2: Arc<ReentrantMutex<()>> = Default::default(); |
126 | let m3: Arc<ReentrantMutex<()>> = Default::default(); |
127 | let b = Arc::new(Barrier::new(4)); |
128 | |
129 | let m1_ = m1.clone(); |
130 | let m2_ = m2.clone(); |
131 | let m3_ = m3.clone(); |
132 | let b1 = b.clone(); |
133 | let b2 = b.clone(); |
134 | let b3 = b.clone(); |
135 | |
136 | assert!(!check_deadlock()); |
137 | |
138 | let _t1 = thread::spawn(move || { |
139 | let _g = m1.lock(); |
140 | let _g = m1.lock(); |
141 | b1.wait(); |
142 | let _ = m2_.lock(); |
143 | }); |
144 | |
145 | let _t2 = thread::spawn(move || { |
146 | let _g = m2.lock(); |
147 | let _g = m2.lock(); |
148 | b2.wait(); |
149 | let _ = m3_.lock(); |
150 | }); |
151 | |
152 | let _t3 = thread::spawn(move || { |
153 | let _g = m3.lock(); |
154 | let _g = m3.lock(); |
155 | b3.wait(); |
156 | let _ = m1_.lock(); |
157 | }); |
158 | |
159 | assert!(!check_deadlock()); |
160 | |
161 | b.wait(); |
162 | sleep(Duration::from_millis(50)); |
163 | assert!(check_deadlock()); |
164 | |
165 | assert!(!check_deadlock()); |
166 | } |
167 | |
168 | #[test] |
169 | fn test_rwlock_deadlock() { |
170 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
171 | |
172 | let m1: Arc<RwLock<()>> = Default::default(); |
173 | let m2: Arc<RwLock<()>> = Default::default(); |
174 | let m3: Arc<RwLock<()>> = Default::default(); |
175 | let b = Arc::new(Barrier::new(4)); |
176 | |
177 | let m1_ = m1.clone(); |
178 | let m2_ = m2.clone(); |
179 | let m3_ = m3.clone(); |
180 | let b1 = b.clone(); |
181 | let b2 = b.clone(); |
182 | let b3 = b.clone(); |
183 | |
184 | assert!(!check_deadlock()); |
185 | |
186 | let _t1 = thread::spawn(move || { |
187 | let _g = m1.read(); |
188 | b1.wait(); |
189 | let _g = m2_.write(); |
190 | }); |
191 | |
192 | let _t2 = thread::spawn(move || { |
193 | let _g = m2.read(); |
194 | b2.wait(); |
195 | let _g = m3_.write(); |
196 | }); |
197 | |
198 | let _t3 = thread::spawn(move || { |
199 | let _g = m3.read(); |
200 | b3.wait(); |
201 | let _ = m1_.write(); |
202 | }); |
203 | |
204 | assert!(!check_deadlock()); |
205 | |
206 | b.wait(); |
207 | sleep(Duration::from_millis(50)); |
208 | assert!(check_deadlock()); |
209 | |
210 | assert!(!check_deadlock()); |
211 | } |
212 | |
213 | #[cfg (rwlock_deadlock_detection_not_supported)] |
214 | #[test] |
215 | fn test_rwlock_deadlock_reentrant() { |
216 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
217 | |
218 | let m1: Arc<RwLock<()>> = Default::default(); |
219 | |
220 | assert!(!check_deadlock()); |
221 | |
222 | let _t1 = thread::spawn(move || { |
223 | let _g = m1.read(); |
224 | let _ = m1.write(); |
225 | }); |
226 | |
227 | sleep(Duration::from_millis(50)); |
228 | assert!(check_deadlock()); |
229 | |
230 | assert!(!check_deadlock()); |
231 | } |
232 | } |
233 | |