1use crate::MutexLike;
6use fuchsia_sync::{
7 MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard, MutexGuard, RwLockReadGuard,
8 RwLockWriteGuard,
9};
10use std::marker::PhantomData;
11
12#[cfg(feature = "detect_lock_dep_cycles")]
13mod tracking {
14 use std::cell::RefCell;
15
16 struct HeldLock {
18 encoded_value: usize,
20 active_subclass_tokens: usize,
22 name: &'static str,
24 }
25
26 struct ThreadState {
28 held_locks: Vec<HeldLock>,
30 }
31
32 thread_local! {
33 static STATE: RefCell<ThreadState> = const { RefCell::new(ThreadState {
34 held_locks: Vec::new(),
35 }) };
36 }
37
38 #[inline(always)]
43 fn check_and_push_lock(target_value: usize, name: &'static str) {
44 STATE.with(|state| {
45 let mut s = state.borrow_mut();
46 if let Some(last) = s.held_locks.last() {
47 let last_value = last.encoded_value;
48 let last_level = last_value & !0xF;
49 let target_level = target_value & !0xF;
50
51 if target_value == last_value {
52 panic!(
53 "LockDep: Self-deadlock detected on lock '{name}' (level {target_value})!"
54 );
55 }
56 if target_level < last_level {
57 panic!(
58 "Invalid lock ordering cycle detected: attempted to acquire '{name}' \
59 after '{}' ({target_level} < {last_level})!",
60 last.name
61 );
62 }
63 if target_level == last_level {
64 if last.active_subclass_tokens == 0 {
66 panic!(
67 "LockDep: Subclassing not allowed or already consumed for lock '{}'",
68 last.name
69 );
70 }
71 }
72 }
73 s.held_locks.push(HeldLock {
74 encoded_value: target_value,
75 active_subclass_tokens: 0,
76 name,
77 });
78 });
79 }
80
81 #[inline(always)]
83 fn pop_lock(target_value: usize) {
84 STATE.with(|state| {
85 let mut s = state.borrow_mut();
86 let Some(pos) = s.held_locks.iter().rposition(|v| v.encoded_value == target_value)
87 else {
88 panic!(
89 "LockDep: Attempted to pop a tracked lock that was not tracked. \
90 Discrepancy detected. Target Lock : {target_value}"
91 );
92 };
93 let lock = &s.held_locks[pos];
94 if lock.active_subclass_tokens > 0 {
95 let stack_str = s
96 .held_locks
97 .iter()
98 .map(|v| format!("{:X}:{}", v.encoded_value, v.active_subclass_tokens))
99 .collect::<Vec<_>>()
100 .join(", ");
101 panic!(
102 "LockDep: Attempted to drop a lock with active subclass tokens! \
103 Target: {:X}, tokens: {}, Stack: [{}]",
104 target_value, lock.active_subclass_tokens, stack_str
105 );
106 }
107 s.held_locks.remove(pos);
108 });
109 }
110
111 #[cfg(test)]
112 pub fn clear_state() {
113 STATE.with(|state| state.borrow_mut().held_locks.clear());
114 }
115
116 #[inline(always)]
120 fn get_subclass(lock_id: usize) -> u8 {
121 STATE.with(|state| {
122 let s = state.borrow();
123 if let Some(last) = s.held_locks.last() {
124 let last_lock_id = last.encoded_value & !0xF;
125 if last_lock_id == lock_id && last.active_subclass_tokens > 0 {
126 return (last.encoded_value & 0xF) as u8 + 1;
127 }
128 }
129 0
130 })
131 }
132
133 #[inline(always)]
137 fn enable_subclass_for_maximal() -> usize {
138 STATE.with(|state| {
139 let mut s = state.borrow_mut();
140 if let Some(last) = s.held_locks.last_mut() {
141 last.active_subclass_tokens += 1;
142 last.encoded_value
143 } else {
144 usize::MAX
146 }
147 })
148 }
149
150 #[inline(always)]
152 fn disable_subclass(encoded_value: usize) {
153 if encoded_value == usize::MAX {
154 return;
155 }
156 STATE.with(|state| {
157 let mut s = state.borrow_mut();
158 let Some(pos) = s.held_locks.iter().rposition(|v| v.encoded_value == encoded_value)
159 else {
160 panic!(
161 "LockDep: Attempted to disable subclass for a lock that is not on the stack! \
162 Value: {:X}",
163 encoded_value
164 );
165 };
166 let lock = &mut s.held_locks[pos];
167 if lock.active_subclass_tokens == 0 {
168 panic!(
169 "LockDep: Attempted to disable subclass for a lock with no active tokens! \
170 Value: {:X}",
171 encoded_value
172 );
173 }
174 lock.active_subclass_tokens -= 1;
175 });
176 }
177
178 pub struct LockLevelToken {
181 pub(crate) target_value: usize,
182 }
183
184 impl LockLevelToken {
185 pub fn new(lock_id: usize, name: &'static str) -> Self {
186 let subclass = get_subclass(lock_id);
187 assert!(subclass < 16, "subclass must be between 0 and 15");
188 let target_value = lock_id | (subclass as usize & 0xF);
189 check_and_push_lock(target_value, name);
190 Self { target_value }
191 }
192 }
193
194 pub struct DynamicLockTracking {
196 pub(crate) lock_id: usize,
197 pub(crate) name: &'static str,
198 }
199
200 impl DynamicLockTracking {
201 pub const fn new(lock_id: usize, name: &'static str) -> Self {
202 Self { lock_id, name }
203 }
204
205 pub fn lock_id(&self) -> usize {
206 self.lock_id
207 }
208
209 pub fn name(&self) -> &'static str {
210 self.name
211 }
212 }
213
214 impl Drop for LockLevelToken {
215 fn drop(&mut self) {
216 pop_lock(self.target_value);
217 }
218 }
219
220 pub struct SubclassToken {
223 encoded_value: usize,
224 }
225
226 impl SubclassToken {
227 pub fn new() -> Self {
228 let encoded_value = enable_subclass_for_maximal();
229 Self { encoded_value }
230 }
231 }
232
233 impl Drop for SubclassToken {
234 fn drop(&mut self) {
235 disable_subclass(self.encoded_value);
236 }
237 }
238}
239
240#[cfg(not(feature = "detect_lock_dep_cycles"))]
241mod tracking {
242 pub struct LockLevelToken {}
245
246 impl LockLevelToken {
247 #[inline(always)]
248 pub fn new(_lock_id: usize, _name: &'static str) -> Self {
249 Self {}
250 }
251 }
252
253 pub struct DynamicLockTracking {}
255
256 impl DynamicLockTracking {
257 pub const fn new(_lock_id: usize, _name: &'static str) -> Self {
258 Self {}
259 }
260
261 pub fn lock_id(&self) -> usize {
262 0
263 }
264
265 pub fn name(&self) -> &'static str {
266 ""
267 }
268 }
269
270 pub struct SubclassToken {}
271
272 impl SubclassToken {
273 #[inline(always)]
274 pub fn new() -> Self {
275 Self {}
276 }
277 }
278}
279
280pub struct DynamicLockDepMutex<T> {
282 pub(crate) inner: fuchsia_sync::Mutex<T>,
283 pub(crate) tracking: tracking::DynamicLockTracking,
284}
285
286impl<T: std::fmt::Debug> std::fmt::Debug for DynamicLockDepMutex<T> {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 write!(f, "DynamicLockDepMutex({:?})", self.inner)
289 }
290}
291
292impl<T> DynamicLockDepMutex<T> {
293 pub const fn new<L: crate::LockLevel>(value: T) -> Self {
294 Self {
295 inner: fuchsia_sync::Mutex::new(value),
296 tracking: tracking::DynamicLockTracking::new(L::LOCK_ID, L::NAME),
297 }
298 }
299
300 #[inline(always)]
301 pub fn lock(&self) -> LockDepGuard<'_, T> {
302 let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
303 LockDepGuard { inner: self.inner.lock(), token }
304 }
305
306 pub fn get_mut(&mut self) -> &mut T {
311 self.inner.get_mut()
312 }
313
314 pub fn into_inner(self) -> T {
316 self.inner.into_inner()
317 }
318}
319
320impl<T> MutexLike for DynamicLockDepMutex<T> {
321 type Guard<'a>
322 = LockDepGuard<'a, T>
323 where
324 T: 'a;
325 #[inline(always)]
326 fn lock(&self, level: usize) -> Self::Guard<'_> {
327 let _token;
328 if level > 0 {
329 _token = allow_subclass();
330 }
331 return self.lock();
332 }
333}
334
335pub struct LockDepGuard<'a, T> {
336 pub(crate) inner: MutexGuard<'a, T>,
337 token: tracking::LockLevelToken,
338}
339
340impl<'a, T> std::ops::Deref for LockDepGuard<'a, T> {
341 type Target = T;
342 fn deref(&self) -> &T {
343 self.inner.deref()
344 }
345}
346
347impl<'a, T> std::ops::DerefMut for LockDepGuard<'a, T> {
348 fn deref_mut(&mut self) -> &mut T {
349 self.inner.deref_mut()
350 }
351}
352
353pub struct LockDepMutex<T, L> {
355 inner: DynamicLockDepMutex<T>,
356 _level: PhantomData<L>,
357}
358
359impl<T: std::fmt::Debug, L> std::fmt::Debug for LockDepMutex<T, L> {
360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361 write!(f, "LockDepMutex({:?}, {})", self.inner.inner, std::any::type_name::<L>())
362 }
363}
364
365impl<T, L: crate::LockLevel> LockDepMutex<T, L> {
366 pub const fn new(value: T) -> Self {
367 Self { inner: DynamicLockDepMutex::new::<L>(value), _level: PhantomData }
368 }
369
370 #[inline(always)]
371 pub fn lock(&self) -> LockDepGuard<'_, T> {
372 self.inner.lock()
373 }
374
375 pub fn get_mut(&mut self) -> &mut T {
380 self.inner.get_mut()
381 }
382
383 pub fn into_inner(self) -> T {
385 self.inner.into_inner()
386 }
387}
388
389impl<T, L> MutexLike for LockDepMutex<T, L> {
390 type Guard<'a>
391 = LockDepGuard<'a, T>
392 where
393 T: 'a,
394 L: 'a;
395 #[inline(always)]
396 fn lock(&self, level: usize) -> Self::Guard<'_> {
397 MutexLike::lock(&self.inner, level)
398 }
399}
400
401impl<T, L: crate::LockLevel> From<T> for LockDepMutex<T, L> {
402 fn from(value: T) -> Self {
403 Self::new(value)
404 }
405}
406
407impl<T: Default, L: crate::LockLevel> Default for LockDepMutex<T, L> {
408 fn default() -> Self {
409 Self::new(T::default())
410 }
411}
412
413pub struct MappedLockDepGuard<'a, T: ?Sized> {
414 pub(crate) inner: MappedMutexGuard<'a, T>,
415 _token: tracking::LockLevelToken,
416}
417
418impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepGuard<'a, T> {
419 type Target = T;
420
421 fn deref(&self) -> &Self::Target {
422 &self.inner
423 }
424}
425
426impl<'a, T: ?Sized> std::ops::DerefMut for MappedLockDepGuard<'a, T> {
427 fn deref_mut(&mut self) -> &mut Self::Target {
428 &mut self.inner
429 }
430}
431
432impl<'a, T> LockDepGuard<'a, T> {
433 pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepGuard<'a, U>
434 where
435 F: FnOnce(&mut T) -> &mut U,
436 {
437 let token = guard.token;
438 let inner = MutexGuard::map(guard.inner, f);
439 MappedLockDepGuard { inner, _token: token }
440 }
441}
442
443pub struct DynamicLockDepRwLock<T> {
445 pub(crate) inner: fuchsia_sync::RwLock<T>,
446 pub(crate) tracking: tracking::DynamicLockTracking,
447}
448
449impl<T: std::fmt::Debug> std::fmt::Debug for DynamicLockDepRwLock<T> {
450 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451 write!(f, "DynamicLockDepRwLock({:?})", self.inner)
452 }
453}
454
455impl<T> DynamicLockDepRwLock<T> {
456 pub const fn new<L: crate::LockLevel>(value: T) -> Self {
457 Self {
458 inner: fuchsia_sync::RwLock::new(value),
459 tracking: tracking::DynamicLockTracking::new(L::LOCK_ID, L::NAME),
460 }
461 }
462
463 #[inline(always)]
464 pub fn read(&self) -> LockDepReadGuard<'_, T> {
465 let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
466 LockDepReadGuard { inner: self.inner.read(), token }
467 }
468
469 #[inline(always)]
470 pub fn write(&self) -> LockDepWriteGuard<'_, T> {
471 let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
472 LockDepWriteGuard { inner: self.inner.write(), token }
473 }
474
475 pub fn get_mut(&mut self) -> &mut T {
480 self.inner.get_mut()
481 }
482
483 pub fn into_inner(self) -> T {
485 self.inner.into_inner()
486 }
487}
488
489pub struct LockDepReadGuard<'a, T> {
490 pub(crate) inner: RwLockReadGuard<'a, T>,
491 token: tracking::LockLevelToken,
492}
493
494impl<'a, T> std::ops::Deref for LockDepReadGuard<'a, T> {
495 type Target = T;
496 fn deref(&self) -> &T {
497 self.inner.deref()
498 }
499}
500
501pub struct LockDepWriteGuard<'a, T> {
502 pub(crate) inner: RwLockWriteGuard<'a, T>,
503 token: tracking::LockLevelToken,
504}
505
506impl<'a, T> std::ops::Deref for LockDepWriteGuard<'a, T> {
507 type Target = T;
508 fn deref(&self) -> &T {
509 self.inner.deref()
510 }
511}
512
513impl<'a, T> std::ops::DerefMut for LockDepWriteGuard<'a, T> {
514 fn deref_mut(&mut self) -> &mut T {
515 self.inner.deref_mut()
516 }
517}
518
519pub struct LockDepRwLock<T, L> {
521 inner: DynamicLockDepRwLock<T>,
522 _level: PhantomData<L>,
523}
524
525impl<T: std::fmt::Debug, L> std::fmt::Debug for LockDepRwLock<T, L> {
526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
527 write!(f, "LockDepRwLock({:?}, {})", self.inner.inner, std::any::type_name::<L>())
528 }
529}
530
531impl<T, L: crate::LockLevel> LockDepRwLock<T, L> {
532 pub const fn new(value: T) -> Self {
533 Self { inner: DynamicLockDepRwLock::new::<L>(value), _level: PhantomData }
534 }
535
536 pub fn get_mut(&mut self) -> &mut T {
541 self.inner.get_mut()
542 }
543
544 pub fn into_inner(self) -> T {
546 self.inner.into_inner()
547 }
548
549 #[inline(always)]
550 pub fn read(&self) -> LockDepReadGuard<'_, T> {
551 self.inner.read()
552 }
553
554 #[inline(always)]
555 pub fn write(&self) -> LockDepWriteGuard<'_, T> {
556 self.inner.write()
557 }
558}
559
560impl<T: Default, L: crate::LockLevel> Default for LockDepRwLock<T, L> {
561 fn default() -> Self {
562 Self::new(T::default())
563 }
564}
565
566impl<T, L: crate::LockLevel> From<T> for LockDepRwLock<T, L> {
567 fn from(value: T) -> Self {
568 Self::new(value)
569 }
570}
571
572pub struct MappedLockDepReadGuard<'a, T: ?Sized> {
573 pub(crate) inner: MappedRwLockReadGuard<'a, T>,
574 _token: tracking::LockLevelToken,
575}
576
577impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepReadGuard<'a, T> {
578 type Target = T;
579
580 fn deref(&self) -> &Self::Target {
581 &self.inner
582 }
583}
584
585pub struct MappedLockDepWriteGuard<'a, T: ?Sized> {
586 pub(crate) inner: MappedRwLockWriteGuard<'a, T>,
587 _token: tracking::LockLevelToken,
588}
589
590impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepWriteGuard<'a, T> {
591 type Target = T;
592
593 fn deref(&self) -> &Self::Target {
594 &self.inner
595 }
596}
597
598impl<'a, T: ?Sized> std::ops::DerefMut for MappedLockDepWriteGuard<'a, T> {
599 fn deref_mut(&mut self) -> &mut Self::Target {
600 &mut self.inner
601 }
602}
603
604impl<'a, T> LockDepReadGuard<'a, T> {
605 pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepReadGuard<'a, U>
606 where
607 F: FnOnce(&T) -> &U,
608 {
609 let token = guard.token;
610 let inner = RwLockReadGuard::map(guard.inner, f);
611 MappedLockDepReadGuard { inner, _token: token }
612 }
613}
614
615impl<'a, T> LockDepWriteGuard<'a, T> {
616 pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepWriteGuard<'a, U>
617 where
618 F: FnOnce(&mut T) -> &mut U,
619 {
620 let token = guard.token;
621 let inner = RwLockWriteGuard::map(guard.inner, f);
622 MappedLockDepWriteGuard { inner, _token: token }
623 }
624}
625
626pub fn allow_subclass() -> tracking::SubclassToken {
630 tracking::SubclassToken::new()
631}
632
633pub fn assert_lock_level<L: crate::LockLevel>() -> tracking::LockLevelToken {
636 tracking::LockLevelToken::new(L::LOCK_ID, L::NAME)
637}
638
639#[cfg(test)]
640#[cfg(feature = "detect_lock_dep_cycles")]
641mod tests {
642 use super::*;
643 use crate::{Unlocked, lock_ordering, ordered_lock, ordered_lock_vec};
644
645 lock_ordering! {
646 Unlocked => LevelA,
647 LevelA => LevelB,
648 }
649
650 #[test]
651 fn test_valid_lock_ordering() {
652 tracking::clear_state();
653 let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
654 let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
655
656 let _guard_a = lock_a.lock();
657 let _guard_b = lock_b.lock();
658 }
659
660 #[test]
661 fn test_subclass_no_lock() {
662 tracking::clear_state();
663 let _token1 = allow_subclass();
664 }
665
666 #[test]
667 fn test_valid_lock_subclass_ordering() {
668 tracking::clear_state();
669 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
670 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
671 let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
672
673 let _guard_a1 = lock_a1.lock();
674 let _token1 = allow_subclass();
675 let _guard_a2 = lock_a2.lock();
676 let _token2 = allow_subclass();
677 let _guard_a3 = lock_a3.lock();
678 }
679
680 #[test]
681 fn test_raii_subclass_guard() {
682 tracking::clear_state();
683 {
684 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
685 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
686
687 let _guard_a1 = lock_a1.lock();
688 let _token = allow_subclass();
689 let _guard_a2 = lock_a2.lock(); }
691 }
692
693 #[test]
694 fn test_subclass_guard_dropped_and_reacquired() {
695 tracking::clear_state();
696 {
697 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
698 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
699
700 let _guard_a1 = lock_a1.lock();
701 let _token1 = allow_subclass();
702 for _ in 0..2 {
703 let _guard_a2 = lock_a2.lock(); }
705 }
706 }
707
708 #[test]
709 fn test_multiple_subclass_same_level() {
710 tracking::clear_state();
711 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
712 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
713
714 let _guard_a1 = lock_a1.lock();
715 let _token1 = allow_subclass();
716 for _ in 0..2 {
717 let _token2 = allow_subclass();
718 let _guard_a2 = lock_a2.lock();
719 }
720 }
721
722 #[test]
723 #[should_panic(expected = "Subclassing not allowed or already consumed")]
724 fn test_raii_subclass_guard_limit() {
725 tracking::clear_state();
726 {
727 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
728 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
729 let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
730
731 let _guard_a1 = lock_a1.lock();
732 let _token = allow_subclass();
733 let _guard_a2 = lock_a2.lock();
734
735 let _guard_a3 = lock_a3.lock();
736 }
737 }
738
739 #[test]
740 fn test_raii_subclass_guard_multiple() {
741 tracking::clear_state();
742 {
743 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
744 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
745 let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
746
747 let _guard_a1 = lock_a1.lock();
748 let _token1 = allow_subclass();
749 let _guard_a2 = lock_a2.lock();
750
751 let _token2 = allow_subclass();
752 let _guard_a3 = lock_a3.lock(); }
754 }
755
756 #[test]
757 #[should_panic(expected = "Invalid lock ordering cycle detected")]
758 fn test_invalid_lock_ordering_cycle() {
759 tracking::clear_state();
760 {
761 let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
762 let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
763
764 let _guard_b = lock_b.lock();
765 let _guard_a = lock_a.lock(); }
767 }
768
769 #[test]
770 #[should_panic(expected = "LockDep: Self-deadlock detected")]
771 fn test_self_deadlock() {
772 tracking::clear_state();
773 {
774 let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
775
776 let _guard_a1 = lock_a.lock();
777 let _guard_a2 = lock_a.lock();
778 }
779 }
780
781 #[test]
782 fn test_subclass_drop_out_of_order() {
783 tracking::clear_state();
784 let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
785 let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
786 let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
787
788 let _guard_a1 = lock_a1.lock();
789 let _token1 = allow_subclass();
790 let _guard_a2 = lock_a2.lock();
791 let _token2 = allow_subclass();
792 let _guard_a3 = lock_a3.lock();
793 std::mem::drop(_token2);
794 std::mem::drop(_guard_a2);
795 std::mem::drop(_guard_a3);
796 let _guard_a2 = lock_a2.lock();
797 }
798
799 #[test]
800 #[should_panic(expected = "LockDep: Attempted to drop a lock with active subclass tokens!")]
801 fn test_drop_lock_with_active_tokens() {
802 tracking::clear_state();
803 let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
804 let guard = lock_a.lock();
805 let _token = allow_subclass();
806 std::mem::drop(guard);
807 }
808
809 #[test]
810 #[should_panic(
811 expected = "Invalid lock ordering cycle detected: attempted to acquire 'LevelA' after 'LevelB'"
812 )]
813 fn test_panic_message_contains_names() {
814 tracking::clear_state();
815 let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
816 let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
817
818 let _guard_b = lock_b.lock();
819 let _guard_a = lock_a.lock();
820 }
821
822 #[test]
823 #[should_panic(expected = "Invalid lock ordering cycle detected")]
824 fn test_assert_lock_level_panic() {
825 tracking::clear_state();
826 let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
827
828 let _guard_b = lock_b.lock();
829 let _token = assert_lock_level::<LevelA>();
832 }
833
834 #[test]
835 fn test_ordered_lock() {
836 tracking::clear_state();
837 let lock1: LockDepMutex<i32, LevelA> = LockDepMutex::new(1);
838 let lock2: LockDepMutex<i32, LevelA> = LockDepMutex::new(2);
839
840 {
841 let (g1, g2) = ordered_lock(&lock1, &lock2);
842 assert_eq!(*g1, 1);
843 assert_eq!(*g2, 2);
844 }
845
846 {
847 let (g2, g1) = ordered_lock(&lock2, &lock1);
848 assert_eq!(*g1, 1);
849 assert_eq!(*g2, 2);
850 }
851 }
852
853 #[test]
854 fn test_ordered_lock_vec() {
855 tracking::clear_state();
856 let l0: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
857 let l1: LockDepMutex<i32, LevelA> = LockDepMutex::new(1);
858 let l2: LockDepMutex<i32, LevelA> = LockDepMutex::new(2);
859
860 {
861 let guards = ordered_lock_vec(&[&l0, &l1, &l2]);
862 assert_eq!(*guards[0], 0);
863 assert_eq!(*guards[1], 1);
864 assert_eq!(*guards[2], 2);
865 }
866
867 {
868 let guards = ordered_lock_vec(&[&l2, &l1, &l0]);
869 assert_eq!(*guards[0], 2);
870 assert_eq!(*guards[1], 1);
871 assert_eq!(*guards[2], 0);
872 }
873 }
874
875 #[test]
876 fn test_dynamic_lockdep_success() {
877 tracking::clear_state();
878 let l1 = DynamicLockDepMutex::new::<LevelA>(1);
879 let l2 = DynamicLockDepMutex::new::<LevelB>(2);
880 let _g1 = l1.lock();
881 let _g2 = l2.lock();
882 }
883
884 #[test]
885 #[should_panic(expected = "Invalid lock ordering cycle detected")]
886 fn test_dynamic_lockdep_failure() {
887 tracking::clear_state();
888 let l1 = DynamicLockDepMutex::new::<LevelA>(1);
889 let l2 = DynamicLockDepMutex::new::<LevelB>(2);
890 let _g2 = l2.lock();
891 let _g1 = l1.lock();
892 }
893
894 #[test]
895 fn test_dynamic_lockdep_subclass() {
896 tracking::clear_state();
897 let l1 = DynamicLockDepMutex::new::<LevelA>(1);
898 let l2 = DynamicLockDepMutex::new::<LevelA>(2);
899 let _g1 = l1.lock();
900 let _subclass = tracking::SubclassToken::new();
901 let _g2 = l2.lock();
902 }
903}