Skip to main content

starnix_sync/
lock_dep_mutex.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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    /// Represents a lock held by the current thread.
17    struct HeldLock {
18        /// The encoded value of the lock (Lock ID | Subclass).
19        encoded_value: usize,
20        /// The count of active subclass tokens for this lock.
21        active_subclass_tokens: usize,
22        /// The name of the lock level.
23        name: &'static str,
24    }
25
26    /// Centralized thread-local state for lockdep tracking.
27    struct ThreadState {
28        /// The stack of currently held locks on this thread.
29        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    /// Verifies that acquiring a lock with `target_value` does not violate lock ordering.
39    /// If valid, pushes the lock onto the thread-local stack.
40    ///
41    /// Panics if a self-deadlock or lock cycle is detected.
42    #[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                    // We are acquiring a sublock!
65                    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    /// Removes a lock from the thread-local stack when it is released.
82    #[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    /// Retrieves the allowed subclass for a given lock ID.
117    ///
118    /// Returns `0` if no subclass is currently authorized.
119    #[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    /// Authorizes an incremented subclass for the currently maximal held lock.
134    ///
135    /// Returns the lock ID and the new subclass level.
136    #[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                // No locks held. Return placeholder.
145                usize::MAX
146            }
147        })
148    }
149
150    /// Revokes the subclass authorization for the given lock ID when a `SubclassToken` is dropped.
151    #[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    /// A token that represents a lock level being held for lockdep purposes.
179    /// This does not actually hold a lock, but updates the lockdep state as if it did.
180    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    /// Tracking information for dynamic locks.
195    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    /// A token that allows the next lock acquisition of the same level as the currently maximal
221    /// held lock to use an incremented subclass.
222    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    /// A token that represents a lock level being held for lockdep purposes.
243    /// This does not actually hold a lock, but updates the lockdep state as if it did.
244    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    /// Tracking information for dynamic locks.
254    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
280/// A Mutex that dynamically enforces lock ordering at runtime, without using types for levels.
281pub 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    /// Returns a mutable reference to the underlying data.
307    ///
308    /// Since this call borrows the `DynamicLockDepMutex` mutably, no actual locking takes place -- the
309    /// borrow checker statically ensures no other threads have access to the `DynamicLockDepMutex`.
310    pub fn get_mut(&mut self) -> &mut T {
311        self.inner.get_mut()
312    }
313
314    /// Consumes the `DynamicLockDepMutex`, returning the underlying data.
315    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
353/// A Mutex that dynamically enforces lock ordering at runtime using types for levels.
354pub 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    /// Returns a mutable reference to the underlying data.
376    ///
377    /// Since this call borrows the `LockDepMutex` mutably, no actual locking takes place -- the
378    /// borrow checker statically ensures no other threads have access to the `LockDepMutex`.
379    pub fn get_mut(&mut self) -> &mut T {
380        self.inner.get_mut()
381    }
382
383    /// Consumes the `LockDepMutex`, returning the underlying data.
384    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
443/// An RwLock that dynamically enforces lock ordering at runtime, without using types for levels.
444pub 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    /// Returns a mutable reference to the underlying data.
476    ///
477    /// Since this call borrows the `DynamicLockDepRwLock` mutably, no actual locking takes place -- the
478    /// borrow checker statically ensures no other threads have access to the `DynamicLockDepRwLock`.
479    pub fn get_mut(&mut self) -> &mut T {
480        self.inner.get_mut()
481    }
482
483    /// Consumes the `DynamicLockDepRwLock`, returning the underlying data.
484    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
519/// An RwLock that dynamically enforces lock ordering at runtime using types for levels.
520pub 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    /// Returns a mutable reference to the underlying data.
537    ///
538    /// Since this call borrows the `LockDepRwLock` mutably, no actual locking takes place -- the
539    /// borrow checker statically ensures no other threads have access to the `LockDepRwLock`.
540    pub fn get_mut(&mut self) -> &mut T {
541        self.inner.get_mut()
542    }
543
544    /// Consumes the `LockDepRwLock`, returning the underlying data.
545    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
626/// A token that allows the next lock acquisition of the same level as the currently maximal
627/// held lock to use an incremented subclass.
628/// Allows subclassing of the currently maximal held lock.
629pub fn allow_subclass() -> tracking::SubclassToken {
630    tracking::SubclassToken::new()
631}
632
633/// Asserts that the current thread can acquire locks at level `L`.
634/// Returns a token that, when held, forces subsequent locks to be after `L`.
635pub 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(); // Should succeed with subclass 1
690        }
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(); // Should succeed with subclass 1
704            }
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(); // Should succeed with subclass 2
753        }
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(); // Should panic because B > A
766        }
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        // LevelA is before LevelB in the ordering.
830        // So asserting LevelA after holding LevelB should panic!
831        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}