]>
Commit | Line | Data |
---|---|---|
b7449926 | 1 | //! \[Experimental\] Deadlock detection |
83c7162d XL |
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 { | |
e1599b0c | 43 | use crate::{Mutex, ReentrantMutex, RwLock}; |
83c7162d | 44 | use std::sync::{Arc, Barrier}; |
b7449926 | 45 | use std::thread::{self, sleep}; |
83c7162d | 46 | use std::time::Duration; |
e1599b0c XL |
47 | |
48 | // We need to serialize these tests since deadlock detection uses global state | |
49 | lazy_static::lazy_static! { | |
50 | static ref DEADLOCK_DETECTION_LOCK: Mutex<()> = Mutex::new(()); | |
51 | } | |
83c7162d XL |
52 | |
53 | fn check_deadlock() -> bool { | |
54 | use parking_lot_core::deadlock::check_deadlock; | |
55 | !check_deadlock().is_empty() | |
56 | } | |
57 | ||
58 | #[test] | |
59 | fn test_mutex_deadlock() { | |
e1599b0c XL |
60 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
61 | ||
83c7162d XL |
62 | let m1: Arc<Mutex<()>> = Default::default(); |
63 | let m2: Arc<Mutex<()>> = Default::default(); | |
64 | let m3: Arc<Mutex<()>> = Default::default(); | |
65 | let b = Arc::new(Barrier::new(4)); | |
66 | ||
67 | let m1_ = m1.clone(); | |
68 | let m2_ = m2.clone(); | |
69 | let m3_ = m3.clone(); | |
70 | let b1 = b.clone(); | |
71 | let b2 = b.clone(); | |
72 | let b3 = b.clone(); | |
73 | ||
74 | assert!(!check_deadlock()); | |
75 | ||
76 | let _t1 = thread::spawn(move || { | |
77 | let _g = m1.lock(); | |
78 | b1.wait(); | |
79 | let _ = m2_.lock(); | |
80 | }); | |
81 | ||
82 | let _t2 = thread::spawn(move || { | |
83 | let _g = m2.lock(); | |
84 | b2.wait(); | |
85 | let _ = m3_.lock(); | |
86 | }); | |
87 | ||
88 | let _t3 = thread::spawn(move || { | |
89 | let _g = m3.lock(); | |
90 | b3.wait(); | |
91 | let _ = m1_.lock(); | |
92 | }); | |
93 | ||
94 | assert!(!check_deadlock()); | |
95 | ||
96 | b.wait(); | |
97 | sleep(Duration::from_millis(50)); | |
98 | assert!(check_deadlock()); | |
99 | ||
100 | assert!(!check_deadlock()); | |
101 | } | |
102 | ||
103 | #[test] | |
104 | fn test_mutex_deadlock_reentrant() { | |
e1599b0c XL |
105 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
106 | ||
83c7162d XL |
107 | let m1: Arc<Mutex<()>> = Default::default(); |
108 | ||
109 | assert!(!check_deadlock()); | |
110 | ||
111 | let _t1 = thread::spawn(move || { | |
112 | let _g = m1.lock(); | |
113 | let _ = m1.lock(); | |
114 | }); | |
115 | ||
116 | sleep(Duration::from_millis(50)); | |
117 | assert!(check_deadlock()); | |
118 | ||
119 | assert!(!check_deadlock()); | |
120 | } | |
121 | ||
122 | #[test] | |
123 | fn test_remutex_deadlock() { | |
e1599b0c XL |
124 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
125 | ||
83c7162d XL |
126 | let m1: Arc<ReentrantMutex<()>> = Default::default(); |
127 | let m2: Arc<ReentrantMutex<()>> = Default::default(); | |
128 | let m3: Arc<ReentrantMutex<()>> = Default::default(); | |
129 | let b = Arc::new(Barrier::new(4)); | |
130 | ||
131 | let m1_ = m1.clone(); | |
132 | let m2_ = m2.clone(); | |
133 | let m3_ = m3.clone(); | |
134 | let b1 = b.clone(); | |
135 | let b2 = b.clone(); | |
136 | let b3 = b.clone(); | |
137 | ||
138 | assert!(!check_deadlock()); | |
139 | ||
140 | let _t1 = thread::spawn(move || { | |
141 | let _g = m1.lock(); | |
142 | let _g = m1.lock(); | |
143 | b1.wait(); | |
144 | let _ = m2_.lock(); | |
145 | }); | |
146 | ||
147 | let _t2 = thread::spawn(move || { | |
148 | let _g = m2.lock(); | |
149 | let _g = m2.lock(); | |
150 | b2.wait(); | |
151 | let _ = m3_.lock(); | |
152 | }); | |
153 | ||
154 | let _t3 = thread::spawn(move || { | |
155 | let _g = m3.lock(); | |
156 | let _g = m3.lock(); | |
157 | b3.wait(); | |
158 | let _ = m1_.lock(); | |
159 | }); | |
160 | ||
161 | assert!(!check_deadlock()); | |
162 | ||
163 | b.wait(); | |
164 | sleep(Duration::from_millis(50)); | |
165 | assert!(check_deadlock()); | |
166 | ||
167 | assert!(!check_deadlock()); | |
168 | } | |
169 | ||
170 | #[test] | |
171 | fn test_rwlock_deadlock() { | |
e1599b0c XL |
172 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
173 | ||
83c7162d XL |
174 | let m1: Arc<RwLock<()>> = Default::default(); |
175 | let m2: Arc<RwLock<()>> = Default::default(); | |
176 | let m3: Arc<RwLock<()>> = Default::default(); | |
177 | let b = Arc::new(Barrier::new(4)); | |
178 | ||
179 | let m1_ = m1.clone(); | |
180 | let m2_ = m2.clone(); | |
181 | let m3_ = m3.clone(); | |
182 | let b1 = b.clone(); | |
183 | let b2 = b.clone(); | |
184 | let b3 = b.clone(); | |
185 | ||
186 | assert!(!check_deadlock()); | |
187 | ||
188 | let _t1 = thread::spawn(move || { | |
189 | let _g = m1.read(); | |
190 | b1.wait(); | |
191 | let _g = m2_.write(); | |
192 | }); | |
193 | ||
194 | let _t2 = thread::spawn(move || { | |
195 | let _g = m2.read(); | |
196 | b2.wait(); | |
197 | let _g = m3_.write(); | |
198 | }); | |
199 | ||
200 | let _t3 = thread::spawn(move || { | |
201 | let _g = m3.read(); | |
202 | b3.wait(); | |
203 | let _ = m1_.write(); | |
204 | }); | |
205 | ||
206 | assert!(!check_deadlock()); | |
207 | ||
208 | b.wait(); | |
209 | sleep(Duration::from_millis(50)); | |
210 | assert!(check_deadlock()); | |
211 | ||
212 | assert!(!check_deadlock()); | |
213 | } | |
214 | ||
e1599b0c | 215 | #[cfg(rwlock_deadlock_detection_not_supported)] |
83c7162d XL |
216 | #[test] |
217 | fn test_rwlock_deadlock_reentrant() { | |
e1599b0c XL |
218 | let _guard = DEADLOCK_DETECTION_LOCK.lock(); |
219 | ||
83c7162d XL |
220 | let m1: Arc<RwLock<()>> = Default::default(); |
221 | ||
222 | assert!(!check_deadlock()); | |
223 | ||
224 | let _t1 = thread::spawn(move || { | |
225 | let _g = m1.read(); | |
226 | let _ = m1.write(); | |
227 | }); | |
228 | ||
229 | sleep(Duration::from_millis(50)); | |
230 | assert!(check_deadlock()); | |
231 | ||
232 | assert!(!check_deadlock()); | |
233 | } | |
234 | } |