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, RwLockLike};
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    use std::rc::Rc;
16
17    /// Represents a lock held by the current thread.
18    struct HeldLock {
19        /// The encoded value of the lock (Lock ID | Subclass).
20        encoded_value: usize,
21        /// The count of active subclass tokens for this lock.
22        active_subclass_tokens: usize,
23        /// The name of the lock level.
24        name: &'static str,
25    }
26
27    /// Centralized thread-local state for lockdep tracking.
28    struct ThreadState {
29        /// The stack of currently held locks on this thread.
30        held_locks: Vec<HeldLock>,
31    }
32
33    thread_local! {
34        static STATE: RefCell<ThreadState> = const { RefCell::new(ThreadState {
35            held_locks: Vec::new(),
36        }) };
37    }
38
39    /// Verifies that acquiring a lock with `target_value` does not violate lock ordering.
40    /// If valid, pushes the lock onto the thread-local stack.
41    ///
42    /// Panics if a self-deadlock or lock cycle is detected.
43    #[inline(always)]
44    fn check_and_push_lock(target_value: usize, name: &'static str) {
45        STATE.with(|state| {
46            let mut s = state.borrow_mut();
47            if let Some(last) = s.held_locks.last() {
48                let last_value = last.encoded_value;
49                let last_level = last_value & !0xF;
50                let target_level = target_value & !0xF;
51
52                if target_value == last_value {
53                    panic!(
54                        "LockDep: Self-deadlock detected on lock '{name}' (level {target_value})!"
55                    );
56                }
57                if target_level < last_level {
58                    panic!(
59                        "Invalid lock ordering cycle detected: attempted to acquire '{name}' \
60                        after '{}' ({target_level} < {last_level})!",
61                        last.name
62                    );
63                }
64                if target_level == last_level {
65                    // We are acquiring a sublock!
66                    if last.active_subclass_tokens == 0 {
67                        panic!(
68                            "LockDep: Subclassing not allowed or already consumed for lock '{}'",
69                            last.name
70                        );
71                    }
72                }
73            }
74            s.held_locks.push(HeldLock {
75                encoded_value: target_value,
76                active_subclass_tokens: 0,
77                name,
78            });
79        });
80    }
81
82    /// Removes a lock from the thread-local stack when it is released.
83    #[inline(always)]
84    fn pop_lock(target_value: usize) {
85        STATE.with(|state| {
86            let mut s = state.borrow_mut();
87            let Some(pos) = s.held_locks.iter().rposition(|v| v.encoded_value == target_value)
88            else {
89                panic!(
90                    "LockDep: Attempted to pop a tracked lock that was not tracked. \
91                    Discrepancy detected. Target Lock : {target_value}"
92                );
93            };
94            let lock = &s.held_locks[pos];
95            if lock.active_subclass_tokens > 0 {
96                let stack_str = s
97                    .held_locks
98                    .iter()
99                    .map(|v| format!("{:X}:{}", v.encoded_value, v.active_subclass_tokens))
100                    .collect::<Vec<_>>()
101                    .join(", ");
102                panic!(
103                    "LockDep: Attempted to drop a lock with active subclass tokens! \
104                        Target: {:X}, tokens: {}, Stack: [{}]",
105                    target_value, lock.active_subclass_tokens, stack_str
106                );
107            }
108            s.held_locks.remove(pos);
109        });
110    }
111
112    #[cfg(test)]
113    pub fn clear_state() {
114        STATE.with(|state| state.borrow_mut().held_locks.clear());
115    }
116
117    /// Retrieves the allowed subclass for a given lock ID.
118    ///
119    /// Returns `0` if no subclass is currently authorized.
120    #[inline(always)]
121    fn get_subclass(lock_id: usize) -> u8 {
122        STATE.with(|state| {
123            let s = state.borrow();
124            if let Some(last) = s.held_locks.last() {
125                let last_lock_id = last.encoded_value & !0xF;
126                if last_lock_id == lock_id && last.active_subclass_tokens > 0 {
127                    return (last.encoded_value & 0xF) as u8 + 1;
128                }
129            }
130            0
131        })
132    }
133
134    /// Authorizes an incremented subclass for the currently maximal held lock.
135    ///
136    /// Returns the lock ID and the new subclass level.
137    #[inline(always)]
138    fn enable_subclass_for_maximal() -> usize {
139        STATE.with(|state| {
140            let mut s = state.borrow_mut();
141            if let Some(last) = s.held_locks.last_mut() {
142                last.active_subclass_tokens += 1;
143                last.encoded_value
144            } else {
145                // No locks held. Return placeholder.
146                usize::MAX
147            }
148        })
149    }
150
151    /// Revokes the subclass authorization for the given lock ID when a `SubclassToken` is dropped.
152    #[inline(always)]
153    fn disable_subclass(encoded_value: usize) {
154        if encoded_value == usize::MAX {
155            return;
156        }
157        STATE.with(|state| {
158            let mut s = state.borrow_mut();
159            let Some(pos) = s.held_locks.iter().rposition(|v| v.encoded_value == encoded_value)
160            else {
161                panic!(
162                    "LockDep: Attempted to disable subclass for a lock that is not on the stack! \
163                    Value: {:X}",
164                    encoded_value
165                );
166            };
167            let lock = &mut s.held_locks[pos];
168            if lock.active_subclass_tokens == 0 {
169                panic!(
170                    "LockDep: Attempted to disable subclass for a lock with no active tokens! \
171                    Value: {:X}",
172                    encoded_value
173                );
174            }
175            lock.active_subclass_tokens -= 1;
176        });
177    }
178
179    /// A token that represents a lock level being held for lockdep purposes.
180    /// This does not actually hold a lock, but updates the lockdep state as if it did.
181    #[derive(Clone)]
182    pub struct LockLevelToken {
183        inner: Rc<InternalLockLevelToken>,
184    }
185
186    struct InternalLockLevelToken {
187        target_value: usize,
188    }
189
190    impl LockLevelToken {
191        pub(super) fn new(lock_id: usize, name: &'static str) -> Self {
192            let subclass = get_subclass(lock_id);
193            assert!(subclass < 16, "subclass must be between 0 and 15");
194            let target_value = lock_id | (subclass as usize & 0xF);
195            check_and_push_lock(target_value, name);
196            Self { inner: Rc::new(InternalLockLevelToken { target_value }) }
197        }
198
199        fn target_value(&self) -> usize {
200            self.inner.target_value
201        }
202    }
203
204    /// Tracking information for dynamic locks.
205    pub struct DynamicLockTracking {
206        lock_id: usize,
207        name: &'static str,
208    }
209
210    impl DynamicLockTracking {
211        pub(super) const fn new(lock_id: usize, name: &'static str) -> Self {
212            Self { lock_id, name }
213        }
214
215        pub(super) fn lock_id(&self) -> usize {
216            self.lock_id
217        }
218
219        pub(super) fn name(&self) -> &'static str {
220            self.name
221        }
222    }
223
224    impl Drop for InternalLockLevelToken {
225        fn drop(&mut self) {
226            pop_lock(self.target_value);
227        }
228    }
229
230    /// A token that allows the next lock acquisition of the same level as the currently maximal
231    /// held lock to use an incremented subclass.
232    pub struct SubclassToken {
233        encoded_value: usize,
234    }
235
236    impl SubclassToken {
237        pub(super) fn new() -> Self {
238            let encoded_value = enable_subclass_for_maximal();
239            Self { encoded_value }
240        }
241    }
242
243    impl Drop for SubclassToken {
244        fn drop(&mut self) {
245            disable_subclass(self.encoded_value);
246        }
247    }
248
249    #[derive(Default)]
250    pub struct LockDepContext {
251        token: Option<LockLevelToken>,
252    }
253
254    pub(super) fn lock_with_context<'a, T>(
255        mutex: &'a crate::DynamicLockDepMutex<T>,
256        context: &mut LockDepContext,
257    ) -> crate::LockDepGuard<'a, T> {
258        match &mut context.token {
259            token @ None => {
260                let guard = mutex.lock();
261                *token = Some(guard.token.clone());
262                guard
263            }
264            Some(token) => {
265                assert_eq!(
266                    mutex.tracking.lock_id(),
267                    token.target_value() & !0xF,
268                    "LockDep: Cannot mix different lock levels in ordered_lock_vec"
269                );
270                let inner = mutex.inner.lock();
271                crate::LockDepGuard { inner, token: token.clone() }
272            }
273        }
274    }
275
276    pub(super) fn read_with_context<'a, T>(
277        rwlock: &'a crate::DynamicLockDepRwLock<T>,
278        context: &mut LockDepContext,
279    ) -> crate::LockDepReadGuard<'a, T> {
280        match &mut context.token {
281            token @ None => {
282                let guard = rwlock.read();
283                *token = Some(guard.token.clone());
284                guard
285            }
286            Some(token) => {
287                assert_eq!(
288                    rwlock.tracking.lock_id(),
289                    token.target_value() & !0xF,
290                    "LockDep: Cannot mix different lock levels in ordered_lock_vec"
291                );
292                let inner = rwlock.inner.read();
293                crate::LockDepReadGuard { inner, token: token.clone() }
294            }
295        }
296    }
297
298    pub(super) fn write_with_context<'a, T>(
299        rwlock: &'a crate::DynamicLockDepRwLock<T>,
300        context: &mut LockDepContext,
301    ) -> crate::LockDepWriteGuard<'a, T> {
302        match &mut context.token {
303            token @ None => {
304                let guard = rwlock.write();
305                *token = Some(guard.token.clone());
306                guard
307            }
308            Some(token) => {
309                assert_eq!(
310                    rwlock.tracking.lock_id(),
311                    token.target_value() & !0xF,
312                    "LockDep: Cannot mix different lock levels in ordered_lock_vec"
313                );
314                let inner = rwlock.inner.write();
315                crate::LockDepWriteGuard { inner, token: token.clone() }
316            }
317        }
318    }
319}
320
321#[cfg(not(feature = "detect_lock_dep_cycles"))]
322mod tracking {
323    /// A token that represents a lock level being held for lockdep purposes.
324    /// This does not actually hold a lock, but updates the lockdep state as if it did.
325    #[derive(Clone)]
326    pub struct LockLevelToken {}
327
328    impl LockLevelToken {
329        #[inline(always)]
330        pub(super) fn new(_lock_id: usize, _name: &'static str) -> Self {
331            Self {}
332        }
333    }
334
335    /// Tracking information for dynamic locks.
336    pub struct DynamicLockTracking {}
337
338    impl DynamicLockTracking {
339        pub(super) const fn new(_lock_id: usize, _name: &'static str) -> Self {
340            Self {}
341        }
342
343        pub(super) fn lock_id(&self) -> usize {
344            0
345        }
346
347        pub(super) fn name(&self) -> &'static str {
348            ""
349        }
350    }
351
352    pub struct SubclassToken {}
353
354    impl SubclassToken {
355        #[inline(always)]
356        pub(super) fn new() -> Self {
357            Self {}
358        }
359    }
360
361    pub type LockDepContext = ();
362
363    pub(super) fn lock_with_context<'a, T>(
364        mutex: &'a crate::DynamicLockDepMutex<T>,
365        _context: &mut LockDepContext,
366    ) -> crate::LockDepGuard<'a, T> {
367        mutex.lock()
368    }
369
370    pub(super) fn read_with_context<'a, T>(
371        rwlock: &'a crate::DynamicLockDepRwLock<T>,
372        _context: &mut LockDepContext,
373    ) -> crate::LockDepReadGuard<'a, T> {
374        rwlock.read()
375    }
376
377    pub(super) fn write_with_context<'a, T>(
378        rwlock: &'a crate::DynamicLockDepRwLock<T>,
379        _context: &mut LockDepContext,
380    ) -> crate::LockDepWriteGuard<'a, T> {
381        rwlock.write()
382    }
383}
384
385/// A Mutex that dynamically enforces lock ordering at runtime, without using types for levels.
386pub struct DynamicLockDepMutex<T> {
387    inner: fuchsia_sync::Mutex<T>,
388    tracking: tracking::DynamicLockTracking,
389}
390
391impl<T: std::fmt::Debug> std::fmt::Debug for DynamicLockDepMutex<T> {
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        write!(f, "DynamicLockDepMutex({:?})", self.inner)
394    }
395}
396
397impl<T> DynamicLockDepMutex<T> {
398    pub const fn new<L: crate::LockLevel>(value: T) -> Self {
399        Self {
400            inner: fuchsia_sync::Mutex::new(value),
401            tracking: tracking::DynamicLockTracking::new(L::LOCK_ID, L::NAME),
402        }
403    }
404
405    #[inline(always)]
406    pub fn lock(&self) -> LockDepGuard<'_, T> {
407        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
408        LockDepGuard { inner: self.inner.lock(), token }
409    }
410
411    /// Returns a mutable reference to the underlying data.
412    ///
413    /// Since this call borrows the `DynamicLockDepMutex` mutably, no actual locking takes place -- the
414    /// borrow checker statically ensures no other threads have access to the `DynamicLockDepMutex`.
415    pub fn get_mut(&mut self) -> &mut T {
416        self.inner.get_mut()
417    }
418
419    /// Consumes the `DynamicLockDepMutex`, returning the underlying data.
420    pub fn into_inner(self) -> T {
421        self.inner.into_inner()
422    }
423}
424
425impl<T> MutexLike for DynamicLockDepMutex<T> {
426    type Guard<'a>
427        = LockDepGuard<'a, T>
428    where
429        T: 'a;
430    type Context = tracking::LockDepContext;
431
432    #[inline(always)]
433    fn context() -> Self::Context {
434        Default::default()
435    }
436
437    #[inline(always)]
438    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
439        tracking::lock_with_context(self, context)
440    }
441}
442
443pub struct LockDepGuard<'a, T> {
444    inner: MutexGuard<'a, T>,
445    token: tracking::LockLevelToken,
446}
447
448impl<'a, T> std::ops::Deref for LockDepGuard<'a, T> {
449    type Target = T;
450    fn deref(&self) -> &T {
451        self.inner.deref()
452    }
453}
454
455impl<'a, T> std::ops::DerefMut for LockDepGuard<'a, T> {
456    fn deref_mut(&mut self) -> &mut T {
457        self.inner.deref_mut()
458    }
459}
460
461/// A Mutex that dynamically enforces lock ordering at runtime using types for levels.
462pub struct LockDepMutex<T, L> {
463    inner: DynamicLockDepMutex<T>,
464    _level: PhantomData<L>,
465}
466
467impl<T: std::fmt::Debug, L> std::fmt::Debug for LockDepMutex<T, L> {
468    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469        write!(f, "LockDepMutex({:?}, {})", self.inner.inner, std::any::type_name::<L>())
470    }
471}
472
473impl<T, L: crate::LockLevel> LockDepMutex<T, L> {
474    pub const fn new(value: T) -> Self {
475        Self { inner: DynamicLockDepMutex::new::<L>(value), _level: PhantomData }
476    }
477
478    #[inline(always)]
479    pub fn lock(&self) -> LockDepGuard<'_, T> {
480        self.inner.lock()
481    }
482
483    /// Returns a mutable reference to the underlying data.
484    ///
485    /// Since this call borrows the `LockDepMutex` mutably, no actual locking takes place -- the
486    /// borrow checker statically ensures no other threads have access to the `LockDepMutex`.
487    pub fn get_mut(&mut self) -> &mut T {
488        self.inner.get_mut()
489    }
490
491    /// Consumes the `LockDepMutex`, returning the underlying data.
492    pub fn into_inner(self) -> T {
493        self.inner.into_inner()
494    }
495}
496
497impl<T, L> MutexLike for LockDepMutex<T, L> {
498    type Guard<'a>
499        = LockDepGuard<'a, T>
500    where
501        T: 'a,
502        L: 'a;
503    type Context = <DynamicLockDepMutex<T> as MutexLike>::Context;
504
505    #[inline(always)]
506    fn context() -> Self::Context {
507        DynamicLockDepMutex::<T>::context()
508    }
509
510    #[inline(always)]
511    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
512        MutexLike::lock(&self.inner, context)
513    }
514}
515
516impl<T, L: crate::LockLevel> From<T> for LockDepMutex<T, L> {
517    fn from(value: T) -> Self {
518        Self::new(value)
519    }
520}
521
522impl<T: Default, L: crate::LockLevel> Default for LockDepMutex<T, L> {
523    fn default() -> Self {
524        Self::new(T::default())
525    }
526}
527
528pub struct MappedLockDepGuard<'a, T: ?Sized> {
529    inner: MappedMutexGuard<'a, T>,
530    _token: tracking::LockLevelToken,
531}
532
533impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepGuard<'a, T> {
534    type Target = T;
535
536    fn deref(&self) -> &Self::Target {
537        &self.inner
538    }
539}
540
541impl<'a, T: ?Sized> std::ops::DerefMut for MappedLockDepGuard<'a, T> {
542    fn deref_mut(&mut self) -> &mut Self::Target {
543        &mut self.inner
544    }
545}
546
547impl<'a, T> LockDepGuard<'a, T> {
548    pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepGuard<'a, U>
549    where
550        F: FnOnce(&mut T) -> &mut U,
551    {
552        let token = guard.token;
553        let inner = MutexGuard::map(guard.inner, f);
554        MappedLockDepGuard { inner, _token: token }
555    }
556}
557
558/// An RwLock that dynamically enforces lock ordering at runtime, without using types for levels.
559pub struct DynamicLockDepRwLock<T> {
560    inner: fuchsia_sync::RwLock<T>,
561    tracking: tracking::DynamicLockTracking,
562}
563
564impl<T: std::fmt::Debug> std::fmt::Debug for DynamicLockDepRwLock<T> {
565    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566        write!(f, "DynamicLockDepRwLock({:?})", self.inner)
567    }
568}
569
570impl<T> DynamicLockDepRwLock<T> {
571    pub const fn new<L: crate::LockLevel>(value: T) -> Self {
572        Self {
573            inner: fuchsia_sync::RwLock::new(value),
574            tracking: tracking::DynamicLockTracking::new(L::LOCK_ID, L::NAME),
575        }
576    }
577
578    #[inline(always)]
579    pub fn read(&self) -> LockDepReadGuard<'_, T> {
580        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
581        LockDepReadGuard { inner: self.inner.read(), token }
582    }
583
584    #[inline(always)]
585    pub fn write(&self) -> LockDepWriteGuard<'_, T> {
586        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
587        LockDepWriteGuard { inner: self.inner.write(), token }
588    }
589
590    /// Returns a mutable reference to the underlying data.
591    ///
592    /// Since this call borrows the `DynamicLockDepRwLock` mutably, no actual locking takes place -- the
593    /// borrow checker statically ensures no other threads have access to the `DynamicLockDepRwLock`.
594    pub fn get_mut(&mut self) -> &mut T {
595        self.inner.get_mut()
596    }
597
598    /// Consumes the `DynamicLockDepRwLock`, returning the underlying data.
599    pub fn into_inner(self) -> T {
600        self.inner.into_inner()
601    }
602}
603
604impl<T> RwLockLike for DynamicLockDepRwLock<T> {
605    type ReadGuard<'a>
606        = LockDepReadGuard<'a, T>
607    where
608        T: 'a;
609    type WriteGuard<'a>
610        = LockDepWriteGuard<'a, T>
611    where
612        T: 'a;
613    type Context = tracking::LockDepContext;
614
615    #[inline(always)]
616    fn context() -> Self::Context {
617        Default::default()
618    }
619
620    #[inline(always)]
621    fn read(&self, context: &mut Self::Context) -> Self::ReadGuard<'_> {
622        tracking::read_with_context(self, context)
623    }
624
625    #[inline(always)]
626    fn write(&self, context: &mut Self::Context) -> Self::WriteGuard<'_> {
627        tracking::write_with_context(self, context)
628    }
629}
630
631pub struct LockDepReadGuard<'a, T> {
632    inner: RwLockReadGuard<'a, T>,
633    token: tracking::LockLevelToken,
634}
635
636impl<'a, T> std::ops::Deref for LockDepReadGuard<'a, T> {
637    type Target = T;
638    fn deref(&self) -> &T {
639        self.inner.deref()
640    }
641}
642
643pub struct LockDepWriteGuard<'a, T> {
644    inner: RwLockWriteGuard<'a, T>,
645    token: tracking::LockLevelToken,
646}
647
648impl<'a, T> std::ops::Deref for LockDepWriteGuard<'a, T> {
649    type Target = T;
650    fn deref(&self) -> &T {
651        self.inner.deref()
652    }
653}
654
655impl<'a, T> std::ops::DerefMut for LockDepWriteGuard<'a, T> {
656    fn deref_mut(&mut self) -> &mut T {
657        self.inner.deref_mut()
658    }
659}
660
661impl<'a, T> LockDepWriteGuard<'a, T> {
662    pub fn downgrade(guard: Self) -> LockDepReadGuard<'a, T> {
663        let token = guard.token;
664        let inner = RwLockWriteGuard::downgrade(guard.inner);
665        LockDepReadGuard { inner, token }
666    }
667}
668
669/// An RwLock that dynamically enforces lock ordering at runtime using types for levels.
670pub struct LockDepRwLock<T, L> {
671    inner: DynamicLockDepRwLock<T>,
672    _level: PhantomData<L>,
673}
674
675impl<T: std::fmt::Debug, L> std::fmt::Debug for LockDepRwLock<T, L> {
676    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
677        write!(f, "LockDepRwLock({:?}, {})", self.inner.inner, std::any::type_name::<L>())
678    }
679}
680
681impl<T, L: crate::LockLevel> LockDepRwLock<T, L> {
682    pub const fn new(value: T) -> Self {
683        Self { inner: DynamicLockDepRwLock::new::<L>(value), _level: PhantomData }
684    }
685
686    /// Returns a mutable reference to the underlying data.
687    ///
688    /// Since this call borrows the `LockDepRwLock` mutably, no actual locking takes place -- the
689    /// borrow checker statically ensures no other threads have access to the `LockDepRwLock`.
690    pub fn get_mut(&mut self) -> &mut T {
691        self.inner.get_mut()
692    }
693
694    /// Consumes the `LockDepRwLock`, returning the underlying data.
695    pub fn into_inner(self) -> T {
696        self.inner.into_inner()
697    }
698
699    #[inline(always)]
700    pub fn read(&self) -> LockDepReadGuard<'_, T> {
701        self.inner.read()
702    }
703
704    #[inline(always)]
705    pub fn write(&self) -> LockDepWriteGuard<'_, T> {
706        self.inner.write()
707    }
708}
709
710impl<T, L> RwLockLike for LockDepRwLock<T, L> {
711    type ReadGuard<'a>
712        = LockDepReadGuard<'a, T>
713    where
714        T: 'a,
715        L: 'a;
716    type WriteGuard<'a>
717        = LockDepWriteGuard<'a, T>
718    where
719        T: 'a,
720        L: 'a;
721    type Context = <DynamicLockDepRwLock<T> as RwLockLike>::Context;
722
723    #[inline(always)]
724    fn context() -> Self::Context {
725        DynamicLockDepRwLock::<T>::context()
726    }
727
728    #[inline(always)]
729    fn read(&self, context: &mut Self::Context) -> Self::ReadGuard<'_> {
730        RwLockLike::read(&self.inner, context)
731    }
732
733    #[inline(always)]
734    fn write(&self, context: &mut Self::Context) -> Self::WriteGuard<'_> {
735        RwLockLike::write(&self.inner, context)
736    }
737}
738
739impl<T: Default, L: crate::LockLevel> Default for LockDepRwLock<T, L> {
740    fn default() -> Self {
741        Self::new(T::default())
742    }
743}
744
745impl<T, L: crate::LockLevel> From<T> for LockDepRwLock<T, L> {
746    fn from(value: T) -> Self {
747        Self::new(value)
748    }
749}
750
751pub struct MappedLockDepReadGuard<'a, T: ?Sized> {
752    inner: MappedRwLockReadGuard<'a, T>,
753    _token: tracking::LockLevelToken,
754}
755
756impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepReadGuard<'a, T> {
757    type Target = T;
758
759    fn deref(&self) -> &Self::Target {
760        &self.inner
761    }
762}
763
764pub struct MappedLockDepWriteGuard<'a, T: ?Sized> {
765    inner: MappedRwLockWriteGuard<'a, T>,
766    _token: tracking::LockLevelToken,
767}
768
769impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepWriteGuard<'a, T> {
770    type Target = T;
771
772    fn deref(&self) -> &Self::Target {
773        &self.inner
774    }
775}
776
777impl<'a, T: ?Sized> std::ops::DerefMut for MappedLockDepWriteGuard<'a, T> {
778    fn deref_mut(&mut self) -> &mut Self::Target {
779        &mut self.inner
780    }
781}
782
783impl<'a, T> LockDepReadGuard<'a, T> {
784    pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepReadGuard<'a, U>
785    where
786        F: FnOnce(&T) -> &U,
787    {
788        let token = guard.token;
789        let inner = RwLockReadGuard::map(guard.inner, f);
790        MappedLockDepReadGuard { inner, _token: token }
791    }
792}
793
794impl<'a, T> LockDepWriteGuard<'a, T> {
795    pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepWriteGuard<'a, U>
796    where
797        F: FnOnce(&mut T) -> &mut U,
798    {
799        let token = guard.token;
800        let inner = RwLockWriteGuard::map(guard.inner, f);
801        MappedLockDepWriteGuard { inner, _token: token }
802    }
803}
804
805/// A token that allows the next lock acquisition of the same level as the currently maximal
806/// held lock to use an incremented subclass.
807/// Allows subclassing of the currently maximal held lock.
808pub fn allow_subclass() -> tracking::SubclassToken {
809    tracking::SubclassToken::new()
810}
811
812/// Asserts that the current thread can acquire locks at level `L`.
813/// Returns a token that, when held, forces subsequent locks to be after `L`.
814pub fn assert_lock_level<L: crate::LockLevel>() -> tracking::LockLevelToken {
815    tracking::LockLevelToken::new(L::LOCK_ID, L::NAME)
816}
817
818#[cfg(test)]
819#[cfg(feature = "detect_lock_dep_cycles")]
820mod tests {
821    use super::*;
822    use crate::{Unlocked, lock_ordering, ordered_lock, ordered_lock_vec};
823
824    lock_ordering! {
825        Unlocked => LevelA,
826        LevelA => LevelB,
827    }
828
829    #[test]
830    fn test_valid_lock_ordering() {
831        tracking::clear_state();
832        let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
833        let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
834
835        let _guard_a = lock_a.lock();
836        let _guard_b = lock_b.lock();
837    }
838
839    #[test]
840    fn test_subclass_no_lock() {
841        tracking::clear_state();
842        let _token1 = allow_subclass();
843    }
844
845    #[test]
846    fn test_valid_lock_subclass_ordering() {
847        tracking::clear_state();
848        let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
849        let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
850        let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
851
852        let _guard_a1 = lock_a1.lock();
853        let _token1 = allow_subclass();
854        let _guard_a2 = lock_a2.lock();
855        let _token2 = allow_subclass();
856        let _guard_a3 = lock_a3.lock();
857    }
858
859    #[test]
860    fn test_raii_subclass_guard() {
861        tracking::clear_state();
862        {
863            let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
864            let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
865
866            let _guard_a1 = lock_a1.lock();
867            let _token = allow_subclass();
868            let _guard_a2 = lock_a2.lock(); // Should succeed with subclass 1
869        }
870    }
871
872    #[test]
873    fn test_subclass_guard_dropped_and_reacquired() {
874        tracking::clear_state();
875        {
876            let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
877            let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
878
879            let _guard_a1 = lock_a1.lock();
880            let _token1 = allow_subclass();
881            for _ in 0..2 {
882                let _guard_a2 = lock_a2.lock(); // Should succeed with subclass 1
883            }
884        }
885    }
886
887    #[test]
888    fn test_multiple_subclass_same_level() {
889        tracking::clear_state();
890        let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
891        let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
892
893        let _guard_a1 = lock_a1.lock();
894        let _token1 = allow_subclass();
895        for _ in 0..2 {
896            let _token2 = allow_subclass();
897            let _guard_a2 = lock_a2.lock();
898        }
899    }
900
901    #[test]
902    #[should_panic(expected = "Subclassing not allowed or already consumed")]
903    fn test_raii_subclass_guard_limit() {
904        tracking::clear_state();
905        {
906            let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
907            let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
908            let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
909
910            let _guard_a1 = lock_a1.lock();
911            let _token = allow_subclass();
912            let _guard_a2 = lock_a2.lock();
913
914            let _guard_a3 = lock_a3.lock();
915        }
916    }
917
918    #[test]
919    fn test_raii_subclass_guard_multiple() {
920        tracking::clear_state();
921        {
922            let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
923            let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
924            let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
925
926            let _guard_a1 = lock_a1.lock();
927            let _token1 = allow_subclass();
928            let _guard_a2 = lock_a2.lock();
929
930            let _token2 = allow_subclass();
931            let _guard_a3 = lock_a3.lock(); // Should succeed with subclass 2
932        }
933    }
934
935    #[test]
936    #[should_panic(expected = "Invalid lock ordering cycle detected")]
937    fn test_invalid_lock_ordering_cycle() {
938        tracking::clear_state();
939        {
940            let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
941            let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
942
943            let _guard_b = lock_b.lock();
944            let _guard_a = lock_a.lock(); // Should panic because B > A
945        }
946    }
947
948    #[test]
949    #[should_panic(expected = "LockDep: Self-deadlock detected")]
950    fn test_self_deadlock() {
951        tracking::clear_state();
952        {
953            let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
954
955            let _guard_a1 = lock_a.lock();
956            let _guard_a2 = lock_a.lock();
957        }
958    }
959
960    #[test]
961    fn test_subclass_drop_out_of_order() {
962        tracking::clear_state();
963        let lock_a1: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
964        let lock_a2: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
965        let lock_a3: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
966
967        let _guard_a1 = lock_a1.lock();
968        let _token1 = allow_subclass();
969        let _guard_a2 = lock_a2.lock();
970        let _token2 = allow_subclass();
971        let _guard_a3 = lock_a3.lock();
972        std::mem::drop(_token2);
973        std::mem::drop(_guard_a2);
974        std::mem::drop(_guard_a3);
975        let _guard_a2 = lock_a2.lock();
976    }
977
978    #[test]
979    #[should_panic(expected = "LockDep: Attempted to drop a lock with active subclass tokens!")]
980    fn test_drop_lock_with_active_tokens() {
981        tracking::clear_state();
982        let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
983        let guard = lock_a.lock();
984        let _token = allow_subclass();
985        std::mem::drop(guard);
986    }
987
988    #[test]
989    #[should_panic(
990        expected = "Invalid lock ordering cycle detected: attempted to acquire 'LevelA' after 'LevelB'"
991    )]
992    fn test_panic_message_contains_names() {
993        tracking::clear_state();
994        let lock_a: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
995        let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
996
997        let _guard_b = lock_b.lock();
998        let _guard_a = lock_a.lock();
999    }
1000
1001    #[test]
1002    #[should_panic(expected = "Invalid lock ordering cycle detected")]
1003    fn test_assert_lock_level_panic() {
1004        tracking::clear_state();
1005        let lock_b: LockDepMutex<i32, LevelB> = LockDepMutex::new(0);
1006
1007        let _guard_b = lock_b.lock();
1008        // LevelA is before LevelB in the ordering.
1009        // So asserting LevelA after holding LevelB should panic!
1010        let _token = assert_lock_level::<LevelA>();
1011    }
1012
1013    #[test]
1014    fn test_ordered_lock() {
1015        tracking::clear_state();
1016        let lock1: LockDepMutex<i32, LevelA> = LockDepMutex::new(1);
1017        let lock2: LockDepMutex<i32, LevelA> = LockDepMutex::new(2);
1018
1019        {
1020            let (g1, g2) = ordered_lock(&lock1, &lock2);
1021            assert_eq!(*g1, 1);
1022            assert_eq!(*g2, 2);
1023        }
1024
1025        {
1026            let (g2, g1) = ordered_lock(&lock2, &lock1);
1027            assert_eq!(*g1, 1);
1028            assert_eq!(*g2, 2);
1029        }
1030    }
1031
1032    #[test]
1033    fn test_ordered_lock_vec() {
1034        tracking::clear_state();
1035        let l0: LockDepMutex<i32, LevelA> = LockDepMutex::new(0);
1036        let l1: LockDepMutex<i32, LevelA> = LockDepMutex::new(1);
1037        let l2: LockDepMutex<i32, LevelA> = LockDepMutex::new(2);
1038
1039        {
1040            let guards = ordered_lock_vec(&[&l0, &l1, &l2]);
1041            assert_eq!(*guards[0], 0);
1042            assert_eq!(*guards[1], 1);
1043            assert_eq!(*guards[2], 2);
1044        }
1045
1046        {
1047            let guards = ordered_lock_vec(&[&l2, &l1, &l0]);
1048            assert_eq!(*guards[0], 2);
1049            assert_eq!(*guards[1], 1);
1050            assert_eq!(*guards[2], 0);
1051        }
1052    }
1053
1054    #[test]
1055    fn test_ordered_lock_vec_many_locks() {
1056        tracking::clear_state();
1057        let locks: Vec<LockDepMutex<i32, LevelA>> = (0..20).map(|i| LockDepMutex::new(i)).collect();
1058        let lock_refs: Vec<&LockDepMutex<i32, LevelA>> = locks.iter().collect();
1059
1060        let guards = ordered_lock_vec(&lock_refs);
1061        assert_eq!(guards.len(), 20);
1062        for i in 0..20 {
1063            assert_eq!(*guards[i], i as i32);
1064        }
1065    }
1066
1067    #[test]
1068    fn test_dynamic_lockdep_success() {
1069        tracking::clear_state();
1070        let l1 = DynamicLockDepMutex::new::<LevelA>(1);
1071        let l2 = DynamicLockDepMutex::new::<LevelB>(2);
1072        let _g1 = l1.lock();
1073        let _g2 = l2.lock();
1074    }
1075
1076    #[test]
1077    #[should_panic(expected = "Invalid lock ordering cycle detected")]
1078    fn test_dynamic_lockdep_failure() {
1079        tracking::clear_state();
1080        let l1 = DynamicLockDepMutex::new::<LevelA>(1);
1081        let l2 = DynamicLockDepMutex::new::<LevelB>(2);
1082        let _g2 = l2.lock();
1083        let _g1 = l1.lock();
1084    }
1085
1086    #[test]
1087    fn test_dynamic_lockdep_subclass() {
1088        tracking::clear_state();
1089        let l1 = DynamicLockDepMutex::new::<LevelA>(1);
1090        let l2 = DynamicLockDepMutex::new::<LevelA>(2);
1091        let _g1 = l1.lock();
1092        let _subclass = tracking::SubclassToken::new();
1093        let _g2 = l2.lock();
1094    }
1095}