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_rcu::RcuDroppable;
7use fuchsia_sync::{
8    MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard, MutexGuard, RwLockReadGuard,
9    RwLockWriteGuard,
10};
11use std::marker::PhantomData;
12
13pub use tracking::LockLevelToken;
14
15#[cfg(feature = "detect_lock_dep_cycles")]
16mod tracking {
17    use fuchsia_rcu::RcuDroppable;
18    use std::cell::RefCell;
19    use std::rc::Rc;
20
21    /// Represents a lock held by the current thread.
22    struct HeldLock {
23        /// The encoded value of the lock (Lock ID | Subclass).
24        encoded_value: usize,
25        /// The count of active subclass tokens for this lock.
26        active_subclass_tokens: usize,
27        /// The name of the lock level.
28        name: &'static str,
29    }
30
31    impl HeldLock {
32        fn new(encoded_value: usize, name: &'static str) -> Self {
33            Self { encoded_value, active_subclass_tokens: 0, name }
34        }
35    }
36
37    /// Centralized thread-local state for lockdep tracking.
38    struct ThreadState {
39        /// The stack of currently held locks on this thread.
40        held_locks: Vec<HeldLock>,
41    }
42
43    thread_local! {
44        static STATE: RefCell<ThreadState> = const { RefCell::new(ThreadState {
45            held_locks: Vec::new(),
46        }) };
47    }
48
49    /// Verifies that acquiring a lock with `target_value` does not violate lock ordering.
50    /// If valid, pushes the lock onto the thread-local stack.
51    ///
52    /// Panics if a self-deadlock or lock cycle is detected.
53    #[inline(always)]
54    #[track_caller]
55    fn check_and_push_lock(lock: HeldLock) {
56        let panic_message = STATE.try_with(|state| {
57            let mut s = state.borrow_mut();
58            if let Some(last) = s.held_locks.last() {
59                let last_value = last.encoded_value;
60                let last_level = last_value & !0xF;
61                let target_value = lock.encoded_value;
62                let target_level = target_value & !0xF;
63                let name = lock.name;
64
65                if target_value == last_value {
66                    if target_level == last_level && name != last.name {
67                        return Err(format!(
68                            "LockDep: Invalid lock ordering detected: acquired lock '{}' while holding lock '{}' (both share the same lock level {})!",
69                            name, last.name, target_value
70                        ));
71                    } else {
72                        return Err(format!(
73                            "LockDep: Self-deadlock detected on lock '{name}' (level {target_value})!"
74                        ));
75                    }
76                }
77                if target_level < last_level {
78                    return Err(format!(
79                        "LockDep: Invalid lock ordering cycle detected: \
80                        attempted to acquire '{name}' after '{}' \
81                        ({target_level} < {last_level})!",
82                        last.name
83                    ));
84                }
85                if target_level == last_level {
86                    // We are acquiring a sublock!
87                    if last.active_subclass_tokens == 0 {
88                        return Err(format!(
89                            "LockDep: Subclassing not allowed or already consumed for lock '{}'",
90                            last.name
91                        ));
92                    }
93                }
94            }
95            s.held_locks.push(lock);
96            Ok(())
97        });
98        if let Ok(Err(panic_message)) = panic_message {
99            panic!("{panic_message}");
100        }
101    }
102
103    /// Removes a lock from the thread-local stack when it is released.
104    #[inline(always)]
105    #[track_caller]
106    fn pop_lock(target_value: usize) -> Option<HeldLock> {
107        let held_lock = STATE.try_with(|state| {
108            let mut s = state.borrow_mut();
109            let Some(pos) = s.held_locks.iter().rposition(|v| v.encoded_value == target_value)
110            else {
111                return Err(format!(
112                    "LockDep: Attempted to pop a tracked lock that was not tracked. \
113                    Discrepancy detected. Target Lock : {target_value}"
114                ));
115            };
116            let lock = &s.held_locks[pos];
117            if lock.active_subclass_tokens > 0 {
118                let stack_str = s
119                    .held_locks
120                    .iter()
121                    .map(|v| format!("{:X}:{}", v.encoded_value, v.active_subclass_tokens))
122                    .collect::<Vec<_>>()
123                    .join(", ");
124                return Err(format!(
125                    "LockDep: Attempted to drop a lock with active subclass tokens! \
126                        Target: {:X}, tokens: {}, Stack: [{}]",
127                    target_value, lock.active_subclass_tokens, stack_str
128                ));
129            }
130            Ok(s.held_locks.remove(pos))
131        });
132        match held_lock.ok()? {
133            Err(panic_message) => {
134                panic!("{panic_message}");
135            }
136            Ok(held_lock) => Some(held_lock),
137        }
138    }
139
140    #[cfg(test)]
141    pub fn clear_state() {
142        STATE.with(|state| state.borrow_mut().held_locks.clear());
143    }
144
145    /// Retrieves the allowed subclass for a given lock ID.
146    ///
147    /// Returns `0` if no subclass is currently authorized.
148    #[inline(always)]
149    fn get_subclass(lock_id: usize) -> u8 {
150        STATE
151            .try_with(|state| {
152                let s = state.borrow();
153                if let Some(last) = s.held_locks.last() {
154                    let last_lock_id = last.encoded_value & !0xF;
155                    if last_lock_id == lock_id && last.active_subclass_tokens > 0 {
156                        return (last.encoded_value & 0xF) as u8 + 1;
157                    }
158                }
159                0
160            })
161            .unwrap_or(0)
162    }
163
164    /// Authorizes an incremented subclass for the currently maximal held lock.
165    ///
166    /// Returns the lock ID and the new subclass level, or 0 if no subclass is
167    /// currently authorized.
168    #[inline(always)]
169    fn enable_subclass_for_maximal() -> usize {
170        STATE
171            .try_with(|state| {
172                let mut s = state.borrow_mut();
173                if let Some(last) = s.held_locks.last_mut() {
174                    last.active_subclass_tokens += 1;
175                    last.encoded_value
176                } else {
177                    // No locks held. Return placeholder.
178                    usize::MAX
179                }
180            })
181            .unwrap_or(0)
182    }
183
184    /// Revokes the subclass authorization for the given lock ID when a `SubclassToken` is dropped.
185    #[inline(always)]
186    #[track_caller]
187    fn disable_subclass(encoded_value: usize) {
188        if encoded_value == usize::MAX {
189            return;
190        }
191        let panic_message = STATE.try_with(|state| {
192            let mut s = state.borrow_mut();
193            let Some(pos) = s.held_locks.iter().rposition(|v| v.encoded_value == encoded_value)
194            else {
195                return Err(format!(
196                    "LockDep: Attempted to disable subclass for a lock that is not on the stack! \
197                    Value: {:X}",
198                    encoded_value
199                ));
200            };
201            let lock = &mut s.held_locks[pos];
202            if lock.active_subclass_tokens == 0 {
203                return Err(format!(
204                    "LockDep: Attempted to disable subclass for a lock with no active tokens! \
205                    Value: {:X}",
206                    encoded_value
207                ));
208            }
209            lock.active_subclass_tokens -= 1;
210            Ok(())
211        });
212        if let Ok(Err(panic_message)) = panic_message {
213            panic!("{panic_message}");
214        }
215    }
216
217    /// A token that represents a lock level being held for lockdep purposes.
218    /// This does not actually hold a lock, but updates the lockdep state as if it did.
219    #[derive(Clone)]
220    pub struct LockLevelToken {
221        inner: Rc<InternalLockLevelToken>,
222    }
223
224    struct InternalLockLevelToken {
225        target_value: usize,
226    }
227
228    impl LockLevelToken {
229        #[track_caller]
230        pub(super) fn new(lock_id: usize, name: &'static str) -> Self {
231            let subclass = get_subclass(lock_id);
232            assert!(subclass < 16, "subclass must be between 0 and 15");
233            let target_value = lock_id | (subclass as usize & 0xF);
234            check_and_push_lock(HeldLock::new(target_value, name));
235            Self { inner: Rc::new(InternalLockLevelToken { target_value }) }
236        }
237
238        fn target_value(&self) -> usize {
239            self.inner.target_value
240        }
241
242        #[track_caller]
243        pub(super) fn check_maximal(&self) {
244            let panic_message = STATE.try_with(|state| {
245                if let Some(last) = state.borrow().held_locks.last() {
246                    if last.encoded_value != self.inner.target_value {
247                        return Err(format!(
248                            "Condvar wait requires the lock to be the latest acquired lock.",
249                        ));
250                    }
251                }
252                Ok(())
253            });
254            if let Ok(Err(panic_message)) = panic_message {
255                panic!("{panic_message}");
256            }
257        }
258
259        pub(super) fn unlock(&self) -> UnlockedGuard {
260            UnlockedGuard::new(self.target_value())
261        }
262    }
263
264    /// A guard that represents the temporary removal of a lock from the thread's
265    /// active lock state for lockdep tracking purposes.
266    pub struct UnlockedGuard {
267        lock: Option<HeldLock>,
268    }
269
270    impl UnlockedGuard {
271        fn new(target_value: usize) -> Self {
272            let lock = pop_lock(target_value);
273            Self { lock }
274        }
275    }
276
277    impl Drop for UnlockedGuard {
278        fn drop(&mut self) {
279            if let Some(lock) = self.lock.take() {
280                check_and_push_lock(lock);
281            }
282        }
283    }
284
285    /// Tracking information for dynamic locks.
286    #[derive(RcuDroppable)]
287    pub struct DynamicLockTracking {
288        lock_id: usize,
289        name: &'static str,
290    }
291
292    impl DynamicLockTracking {
293        pub(super) const fn new(lock_id: usize, name: &'static str) -> Self {
294            Self { lock_id, name }
295        }
296
297        pub(super) fn lock_id(&self) -> usize {
298            self.lock_id
299        }
300
301        pub(super) fn name(&self) -> &'static str {
302            self.name
303        }
304    }
305
306    impl Drop for InternalLockLevelToken {
307        fn drop(&mut self) {
308            pop_lock(self.target_value);
309        }
310    }
311
312    /// A token that allows the next lock acquisition of the same level as the currently maximal
313    /// held lock to use an incremented subclass.
314    pub struct SubclassToken {
315        encoded_value: usize,
316    }
317
318    impl SubclassToken {
319        pub(super) fn new() -> Self {
320            let encoded_value = enable_subclass_for_maximal();
321            Self { encoded_value }
322        }
323    }
324
325    impl Drop for SubclassToken {
326        fn drop(&mut self) {
327            disable_subclass(self.encoded_value);
328        }
329    }
330
331    #[derive(Default)]
332    pub struct LockDepContext {
333        token: Option<LockLevelToken>,
334    }
335
336    #[track_caller]
337    pub(super) fn lock_with_context<'a, T>(
338        mutex: &'a crate::DynamicLockDepMutex<T>,
339        context: &mut LockDepContext,
340    ) -> crate::LockDepGuard<'a, T> {
341        match &mut context.token {
342            token @ None => {
343                let guard = mutex.lock();
344                *token = Some(guard.token.clone());
345                guard
346            }
347            Some(token) => {
348                assert_eq!(
349                    mutex.tracking.lock_id(),
350                    token.target_value() & !0xF,
351                    "LockDep: Cannot mix different lock levels in ordered_lock_vec"
352                );
353                let inner = mutex.inner.lock();
354                crate::LockDepGuard { inner, token: token.clone() }
355            }
356        }
357    }
358
359    #[track_caller]
360    pub(super) fn read_with_context<'a, T>(
361        rwlock: &'a crate::DynamicLockDepRwLock<T>,
362        context: &mut LockDepContext,
363    ) -> crate::LockDepReadGuard<'a, T> {
364        match &mut context.token {
365            token @ None => {
366                let guard = rwlock.read();
367                *token = Some(guard.token.clone());
368                guard
369            }
370            Some(token) => {
371                assert_eq!(
372                    rwlock.tracking.lock_id(),
373                    token.target_value() & !0xF,
374                    "LockDep: Cannot mix different lock levels in ordered_lock_vec"
375                );
376                let inner = rwlock.inner.read();
377                crate::LockDepReadGuard { inner, token: token.clone() }
378            }
379        }
380    }
381
382    #[track_caller]
383    pub(super) fn write_with_context<'a, T>(
384        rwlock: &'a crate::DynamicLockDepRwLock<T>,
385        context: &mut LockDepContext,
386    ) -> crate::LockDepWriteGuard<'a, T> {
387        match &mut context.token {
388            token @ None => {
389                let guard = rwlock.write();
390                *token = Some(guard.token.clone());
391                guard
392            }
393            Some(token) => {
394                assert_eq!(
395                    rwlock.tracking.lock_id(),
396                    token.target_value() & !0xF,
397                    "LockDep: Cannot mix different lock levels in ordered_lock_vec"
398                );
399                let inner = rwlock.inner.write();
400                crate::LockDepWriteGuard { inner, token: token.clone() }
401            }
402        }
403    }
404}
405
406#[cfg(not(feature = "detect_lock_dep_cycles"))]
407mod tracking {
408    use fuchsia_rcu::RcuDroppable;
409
410    /// A token that represents a lock level being held for lockdep purposes.
411    /// This does not actually hold a lock, but updates the lockdep state as if it did.
412    #[derive(Clone)]
413    pub struct LockLevelToken {}
414
415    impl LockLevelToken {
416        #[inline(always)]
417        pub(super) fn new(_lock_id: usize, _name: &'static str) -> Self {
418            Self {}
419        }
420
421        pub(super) fn check_maximal(&self) {}
422
423        pub(super) fn unlock(&self) -> UnlockedGuard {
424            UnlockedGuard {}
425        }
426    }
427
428    /// A guard that represents the temporary removal of a lock from the thread's
429    /// active lock state for lockdep tracking purposes.
430    ///
431    /// This is the no-op implementation used when `detect_lock_dep_cycles` is disabled.
432    pub struct UnlockedGuard {}
433
434    /// Tracking information for dynamic locks.
435    #[derive(RcuDroppable)]
436    pub struct DynamicLockTracking {}
437
438    impl DynamicLockTracking {
439        pub(super) const fn new(_lock_id: usize, _name: &'static str) -> Self {
440            Self {}
441        }
442
443        pub(super) fn lock_id(&self) -> usize {
444            0
445        }
446
447        pub(super) fn name(&self) -> &'static str {
448            ""
449        }
450    }
451
452    pub struct SubclassToken {}
453
454    impl SubclassToken {
455        #[inline(always)]
456        pub(super) fn new() -> Self {
457            Self {}
458        }
459    }
460
461    pub type LockDepContext = ();
462
463    pub(super) fn lock_with_context<'a, T>(
464        mutex: &'a crate::DynamicLockDepMutex<T>,
465        _context: &mut LockDepContext,
466    ) -> crate::LockDepGuard<'a, T> {
467        mutex.lock()
468    }
469
470    pub(super) fn read_with_context<'a, T>(
471        rwlock: &'a crate::DynamicLockDepRwLock<T>,
472        _context: &mut LockDepContext,
473    ) -> crate::LockDepReadGuard<'a, T> {
474        rwlock.read()
475    }
476
477    pub(super) fn write_with_context<'a, T>(
478        rwlock: &'a crate::DynamicLockDepRwLock<T>,
479        _context: &mut LockDepContext,
480    ) -> crate::LockDepWriteGuard<'a, T> {
481        rwlock.write()
482    }
483}
484
485/// A Mutex that dynamically enforces lock ordering at runtime, without using types for levels.
486#[derive(RcuDroppable)]
487pub struct DynamicLockDepMutex<T> {
488    inner: fuchsia_sync::Mutex<T>,
489    tracking: tracking::DynamicLockTracking,
490}
491
492impl<T: std::fmt::Debug> std::fmt::Debug for DynamicLockDepMutex<T> {
493    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
494        write!(f, "DynamicLockDepMutex({:?})", self.inner)
495    }
496}
497
498impl<T> DynamicLockDepMutex<T> {
499    pub const fn new<L: crate::LockLevel>(value: T) -> Self {
500        Self {
501            inner: fuchsia_sync::Mutex::new(value),
502            tracking: tracking::DynamicLockTracking::new(L::LOCK_ID, L::NAME),
503        }
504    }
505
506    #[inline(always)]
507    #[track_caller]
508    pub fn lock(&self) -> LockDepGuard<'_, T> {
509        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
510        LockDepGuard { inner: self.inner.lock(), token }
511    }
512
513    #[inline(always)]
514    #[track_caller]
515    pub fn try_lock(&self) -> Option<LockDepGuard<'_, T>> {
516        let inner = self.inner.try_lock()?;
517        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
518        Some(LockDepGuard { inner, token })
519    }
520
521    /// Returns a mutable reference to the underlying data.
522    ///
523    /// Since this call borrows the `DynamicLockDepMutex` mutably, no actual locking takes place -- the
524    /// borrow checker statically ensures no other threads have access to the `DynamicLockDepMutex`.
525    pub fn get_mut(&mut self) -> &mut T {
526        self.inner.get_mut()
527    }
528
529    /// Consumes the `DynamicLockDepMutex`, returning the underlying data.
530    pub fn into_inner(self) -> T {
531        self.inner.into_inner()
532    }
533}
534
535impl<T> MutexLike for DynamicLockDepMutex<T> {
536    type Guard<'a>
537        = LockDepGuard<'a, T>
538    where
539        T: 'a;
540    type Context = tracking::LockDepContext;
541
542    #[inline(always)]
543    fn context() -> Self::Context {
544        Default::default()
545    }
546
547    #[inline(always)]
548    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
549        tracking::lock_with_context(self, context)
550    }
551}
552
553pub struct LockDepGuard<'a, T> {
554    inner: MutexGuard<'a, T>,
555    token: tracking::LockLevelToken,
556}
557
558impl<'a, T> std::ops::Deref for LockDepGuard<'a, T> {
559    type Target = T;
560    fn deref(&self) -> &T {
561        self.inner.deref()
562    }
563}
564
565impl<'a, T> std::ops::DerefMut for LockDepGuard<'a, T> {
566    fn deref_mut(&mut self) -> &mut T {
567        self.inner.deref_mut()
568    }
569}
570
571impl<'a, T> LockDepGuard<'a, T> {
572    pub(super) fn check_maximal(&self) {
573        self.token.check_maximal();
574    }
575
576    /// Unlock this guard, run the closure, and then re-lock it.
577    pub fn unlocked<F, U>(s: &mut Self, f: F) -> U
578    where
579        F: FnOnce() -> U,
580    {
581        let _guard = s.token.unlock();
582        MutexGuard::unlocked(&mut s.inner, f)
583    }
584}
585
586impl<'a, T> crate::condvar::WaitableMutexGuard<'a, T> for LockDepGuard<'a, T> {
587    fn inner_guard(&mut self, _token: crate::condvar::WaitToken) -> &mut MutexGuard<'a, T> {
588        self.check_maximal();
589        &mut self.inner
590    }
591}
592
593/// A Mutex that dynamically enforces lock ordering at runtime using types for levels.
594#[derive(RcuDroppable)]
595pub struct LockDepMutex<T, L> {
596    inner: DynamicLockDepMutex<T>,
597    _level: PhantomData<L>,
598}
599
600impl<T: std::fmt::Debug, L> std::fmt::Debug for LockDepMutex<T, L> {
601    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602        write!(f, "LockDepMutex({:?}, {})", self.inner.inner, std::any::type_name::<L>())
603    }
604}
605
606impl<T, L: crate::LockLevel> LockDepMutex<T, L> {
607    pub const fn new(value: T) -> Self {
608        Self { inner: DynamicLockDepMutex::new::<L>(value), _level: PhantomData }
609    }
610
611    #[inline(always)]
612    #[track_caller]
613    pub fn lock(&self) -> LockDepGuard<'_, T> {
614        self.inner.lock()
615    }
616
617    #[inline(always)]
618    #[track_caller]
619    pub fn try_lock(&self) -> Option<LockDepGuard<'_, T>> {
620        self.inner.try_lock()
621    }
622
623    /// Returns a mutable reference to the underlying data.
624    ///
625    /// Since this call borrows the `LockDepMutex` mutably, no actual locking takes place -- the
626    /// borrow checker statically ensures no other threads have access to the `LockDepMutex`.
627    pub fn get_mut(&mut self) -> &mut T {
628        self.inner.get_mut()
629    }
630
631    /// Consumes the `LockDepMutex`, returning the underlying data.
632    pub fn into_inner(self) -> T {
633        self.inner.into_inner()
634    }
635}
636
637impl<T, L> MutexLike for LockDepMutex<T, L> {
638    type Guard<'a>
639        = LockDepGuard<'a, T>
640    where
641        T: 'a,
642        L: 'a;
643    type Context = <DynamicLockDepMutex<T> as MutexLike>::Context;
644
645    #[inline(always)]
646    fn context() -> Self::Context {
647        DynamicLockDepMutex::<T>::context()
648    }
649
650    #[inline(always)]
651    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
652        MutexLike::lock(&self.inner, context)
653    }
654}
655
656impl<T, L: crate::LockLevel> From<T> for LockDepMutex<T, L> {
657    fn from(value: T) -> Self {
658        Self::new(value)
659    }
660}
661
662impl<T: Default, L: crate::LockLevel> Default for LockDepMutex<T, L> {
663    fn default() -> Self {
664        Self::new(T::default())
665    }
666}
667
668pub struct MappedLockDepGuard<'a, T: ?Sized> {
669    inner: MappedMutexGuard<'a, T>,
670    _token: tracking::LockLevelToken,
671}
672
673impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepGuard<'a, T> {
674    type Target = T;
675
676    fn deref(&self) -> &Self::Target {
677        &self.inner
678    }
679}
680
681impl<'a, T: ?Sized> std::ops::DerefMut for MappedLockDepGuard<'a, T> {
682    fn deref_mut(&mut self) -> &mut Self::Target {
683        &mut self.inner
684    }
685}
686
687impl<'a, T> LockDepGuard<'a, T> {
688    pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepGuard<'a, U>
689    where
690        F: FnOnce(&mut T) -> &mut U,
691    {
692        let token = guard.token;
693        let inner = MutexGuard::map(guard.inner, f);
694        MappedLockDepGuard { inner, _token: token }
695    }
696}
697
698/// An RwLock that dynamically enforces lock ordering at runtime, without using types for levels.
699#[derive(RcuDroppable)]
700pub struct DynamicLockDepRwLock<T> {
701    inner: fuchsia_sync::RwLock<T>,
702    tracking: tracking::DynamicLockTracking,
703}
704
705impl<T: std::fmt::Debug> std::fmt::Debug for DynamicLockDepRwLock<T> {
706    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
707        write!(f, "DynamicLockDepRwLock({:?})", self.inner)
708    }
709}
710
711impl<T> DynamicLockDepRwLock<T> {
712    pub const fn new<L: crate::LockLevel>(value: T) -> Self {
713        Self {
714            inner: fuchsia_sync::RwLock::new(value),
715            tracking: tracking::DynamicLockTracking::new(L::LOCK_ID, L::NAME),
716        }
717    }
718
719    #[inline(always)]
720    #[track_caller]
721    pub fn read(&self) -> LockDepReadGuard<'_, T> {
722        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
723        LockDepReadGuard { inner: self.inner.read(), token }
724    }
725
726    #[inline(always)]
727    #[track_caller]
728    pub fn try_read(&self) -> Option<LockDepReadGuard<'_, T>> {
729        let inner = self.inner.try_read()?;
730        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
731        Some(LockDepReadGuard { inner, token })
732    }
733
734    #[inline(always)]
735    #[track_caller]
736    pub fn write(&self) -> LockDepWriteGuard<'_, T> {
737        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
738        LockDepWriteGuard { inner: self.inner.write(), token }
739    }
740
741    #[inline(always)]
742    #[track_caller]
743    pub fn try_write(&self) -> Option<LockDepWriteGuard<'_, T>> {
744        let inner = self.inner.try_write()?;
745        let token = tracking::LockLevelToken::new(self.tracking.lock_id(), self.tracking.name());
746        Some(LockDepWriteGuard { inner, token })
747    }
748
749    /// Returns a mutable reference to the underlying data.
750    ///
751    /// Since this call borrows the `DynamicLockDepRwLock` mutably, no actual locking takes place -- the
752    /// borrow checker statically ensures no other threads have access to the `DynamicLockDepRwLock`.
753    pub fn get_mut(&mut self) -> &mut T {
754        self.inner.get_mut()
755    }
756
757    /// Consumes the `DynamicLockDepRwLock`, returning the underlying data.
758    pub fn into_inner(self) -> T {
759        self.inner.into_inner()
760    }
761}
762
763impl<T> RwLockLike for DynamicLockDepRwLock<T> {
764    type ReadGuard<'a>
765        = LockDepReadGuard<'a, T>
766    where
767        T: 'a;
768    type WriteGuard<'a>
769        = LockDepWriteGuard<'a, T>
770    where
771        T: 'a;
772    type Context = tracking::LockDepContext;
773
774    #[inline(always)]
775    fn context() -> Self::Context {
776        Default::default()
777    }
778
779    #[inline(always)]
780    fn read(&self, context: &mut Self::Context) -> Self::ReadGuard<'_> {
781        tracking::read_with_context(self, context)
782    }
783
784    #[inline(always)]
785    fn write(&self, context: &mut Self::Context) -> Self::WriteGuard<'_> {
786        tracking::write_with_context(self, context)
787    }
788}
789
790pub struct LockDepReadGuard<'a, T> {
791    inner: RwLockReadGuard<'a, T>,
792    token: tracking::LockLevelToken,
793}
794
795impl<'a, T> std::ops::Deref for LockDepReadGuard<'a, T> {
796    type Target = T;
797    fn deref(&self) -> &T {
798        self.inner.deref()
799    }
800}
801
802pub struct LockDepWriteGuard<'a, T> {
803    inner: RwLockWriteGuard<'a, T>,
804    token: tracking::LockLevelToken,
805}
806
807impl<'a, T> std::ops::Deref for LockDepWriteGuard<'a, T> {
808    type Target = T;
809    fn deref(&self) -> &T {
810        self.inner.deref()
811    }
812}
813
814impl<'a, T> std::ops::DerefMut for LockDepWriteGuard<'a, T> {
815    fn deref_mut(&mut self) -> &mut T {
816        self.inner.deref_mut()
817    }
818}
819
820impl<'a, T> LockDepWriteGuard<'a, T> {
821    pub fn downgrade(guard: Self) -> LockDepReadGuard<'a, T> {
822        let token = guard.token;
823        let inner = RwLockWriteGuard::downgrade(guard.inner);
824        LockDepReadGuard { inner, token }
825    }
826}
827
828/// An RwLock that dynamically enforces lock ordering at runtime using types for levels.
829#[derive(RcuDroppable)]
830pub struct LockDepRwLock<T, L> {
831    inner: DynamicLockDepRwLock<T>,
832    _level: PhantomData<L>,
833}
834
835impl<T: std::fmt::Debug, L> std::fmt::Debug for LockDepRwLock<T, L> {
836    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
837        write!(f, "LockDepRwLock({:?}, {})", self.inner.inner, std::any::type_name::<L>())
838    }
839}
840
841impl<T, L: crate::LockLevel> LockDepRwLock<T, L> {
842    pub const fn new(value: T) -> Self {
843        Self { inner: DynamicLockDepRwLock::new::<L>(value), _level: PhantomData }
844    }
845
846    /// Returns a mutable reference to the underlying data.
847    ///
848    /// Since this call borrows the `LockDepRwLock` mutably, no actual locking takes place -- the
849    /// borrow checker statically ensures no other threads have access to the `LockDepRwLock`.
850    pub fn get_mut(&mut self) -> &mut T {
851        self.inner.get_mut()
852    }
853
854    /// Consumes the `LockDepRwLock`, returning the underlying data.
855    pub fn into_inner(self) -> T {
856        self.inner.into_inner()
857    }
858
859    #[inline(always)]
860    #[track_caller]
861    pub fn read(&self) -> LockDepReadGuard<'_, T> {
862        self.inner.read()
863    }
864
865    #[inline(always)]
866    #[track_caller]
867    pub fn try_read(&self) -> Option<LockDepReadGuard<'_, T>> {
868        self.inner.try_read()
869    }
870
871    #[inline(always)]
872    #[track_caller]
873    pub fn write(&self) -> LockDepWriteGuard<'_, T> {
874        self.inner.write()
875    }
876
877    #[inline(always)]
878    #[track_caller]
879    pub fn try_write(&self) -> Option<LockDepWriteGuard<'_, T>> {
880        self.inner.try_write()
881    }
882}
883
884impl<T, L> RwLockLike for LockDepRwLock<T, L> {
885    type ReadGuard<'a>
886        = LockDepReadGuard<'a, T>
887    where
888        T: 'a,
889        L: 'a;
890    type WriteGuard<'a>
891        = LockDepWriteGuard<'a, T>
892    where
893        T: 'a,
894        L: 'a;
895    type Context = <DynamicLockDepRwLock<T> as RwLockLike>::Context;
896
897    #[inline(always)]
898    fn context() -> Self::Context {
899        DynamicLockDepRwLock::<T>::context()
900    }
901
902    #[inline(always)]
903    fn read(&self, context: &mut Self::Context) -> Self::ReadGuard<'_> {
904        RwLockLike::read(&self.inner, context)
905    }
906
907    #[inline(always)]
908    fn write(&self, context: &mut Self::Context) -> Self::WriteGuard<'_> {
909        RwLockLike::write(&self.inner, context)
910    }
911}
912
913impl<T: Default, L: crate::LockLevel> Default for LockDepRwLock<T, L> {
914    fn default() -> Self {
915        Self::new(T::default())
916    }
917}
918
919impl<T, L: crate::LockLevel> From<T> for LockDepRwLock<T, L> {
920    fn from(value: T) -> Self {
921        Self::new(value)
922    }
923}
924
925pub struct MappedLockDepReadGuard<'a, T: ?Sized> {
926    inner: MappedRwLockReadGuard<'a, T>,
927    _token: tracking::LockLevelToken,
928}
929
930impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepReadGuard<'a, T> {
931    type Target = T;
932
933    fn deref(&self) -> &Self::Target {
934        &self.inner
935    }
936}
937
938pub struct MappedLockDepWriteGuard<'a, T: ?Sized> {
939    inner: MappedRwLockWriteGuard<'a, T>,
940    _token: tracking::LockLevelToken,
941}
942
943impl<'a, T: ?Sized> std::ops::Deref for MappedLockDepWriteGuard<'a, T> {
944    type Target = T;
945
946    fn deref(&self) -> &Self::Target {
947        &self.inner
948    }
949}
950
951impl<'a, T: ?Sized> std::ops::DerefMut for MappedLockDepWriteGuard<'a, T> {
952    fn deref_mut(&mut self) -> &mut Self::Target {
953        &mut self.inner
954    }
955}
956
957impl<'a, T> LockDepReadGuard<'a, T> {
958    pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepReadGuard<'a, U>
959    where
960        F: FnOnce(&T) -> &U,
961    {
962        let token = guard.token;
963        let inner = RwLockReadGuard::map(guard.inner, f);
964        MappedLockDepReadGuard { inner, _token: token }
965    }
966}
967
968impl<'a, T> LockDepWriteGuard<'a, T> {
969    pub fn map<U: ?Sized, F>(guard: Self, f: F) -> MappedLockDepWriteGuard<'a, U>
970    where
971        F: FnOnce(&mut T) -> &mut U,
972    {
973        let token = guard.token;
974        let inner = RwLockWriteGuard::map(guard.inner, f);
975        MappedLockDepWriteGuard { inner, _token: token }
976    }
977}
978
979/// A token that allows the next lock acquisition of the same level as the currently maximal
980/// held lock to use an incremented subclass.
981/// Allows subclassing of the currently maximal held lock.
982#[track_caller]
983pub fn allow_subclass() -> tracking::SubclassToken {
984    tracking::SubclassToken::new()
985}
986
987/// Asserts that the current thread can acquire locks at level `L`.
988/// Returns a token that, when held, forces subsequent locks to be after `L`.
989#[track_caller]
990pub fn assert_lock_level<L: crate::LockLevel>() -> tracking::LockLevelToken {
991    tracking::LockLevelToken::new(L::LOCK_ID, L::NAME)
992}
993
994#[cfg(test)]
995#[cfg(feature = "detect_lock_dep_cycles")]
996mod tests {
997    use super::*;
998    use crate::{lock_ordering, ordered_lock, ordered_lock_vec};
999
1000    lock_ordering! {
1001        LevelA => LevelB,
1002        Terminal(TerminalC),
1003        Terminal(TerminalD),
1004    }
1005
1006    #[test]
1007    fn test_valid_lock_ordering() {
1008        tracking::clear_state();
1009        let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1010        let lock_b: LockDepMutex<i32, LevelB> = 0.into();
1011        let lock_c: LockDepMutex<i32, TerminalC> = 0.into();
1012        let lock_d: LockDepMutex<i32, TerminalD> = 0.into();
1013
1014        let _guard_a = lock_a.lock();
1015        let _guard_b = lock_b.lock();
1016
1017        {
1018            let _guard_c = lock_c.lock();
1019        }
1020        {
1021            let _guard_d = lock_d.lock();
1022        }
1023    }
1024
1025    #[test]
1026    fn test_subclass_no_lock() {
1027        tracking::clear_state();
1028        let _token1 = allow_subclass();
1029    }
1030
1031    #[test]
1032    fn test_valid_lock_subclass_ordering() {
1033        tracking::clear_state();
1034        let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1035        let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1036        let lock_a3: LockDepMutex<i32, LevelA> = 0.into();
1037
1038        let _guard_a1 = lock_a1.lock();
1039        let _token1 = allow_subclass();
1040        let _guard_a2 = lock_a2.lock();
1041        let _token2 = allow_subclass();
1042        let _guard_a3 = lock_a3.lock();
1043    }
1044
1045    #[test]
1046    fn test_raii_subclass_guard() {
1047        tracking::clear_state();
1048        {
1049            let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1050            let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1051
1052            let _guard_a1 = lock_a1.lock();
1053            let _token = allow_subclass();
1054            let _guard_a2 = lock_a2.lock(); // Should succeed with subclass 1
1055        }
1056    }
1057
1058    #[test]
1059    fn test_subclass_guard_dropped_and_reacquired() {
1060        tracking::clear_state();
1061        {
1062            let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1063            let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1064
1065            let _guard_a1 = lock_a1.lock();
1066            let _token1 = allow_subclass();
1067            for _ in 0..2 {
1068                let _guard_a2 = lock_a2.lock(); // Should succeed with subclass 1
1069            }
1070        }
1071    }
1072
1073    #[test]
1074    fn test_multiple_subclass_same_level() {
1075        tracking::clear_state();
1076        let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1077        let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1078
1079        let _guard_a1 = lock_a1.lock();
1080        let _token1 = allow_subclass();
1081        for _ in 0..2 {
1082            let _token2 = allow_subclass();
1083            let _guard_a2 = lock_a2.lock();
1084        }
1085    }
1086
1087    #[test]
1088    #[should_panic(expected = "Subclassing not allowed or already consumed")]
1089    fn test_raii_subclass_guard_limit() {
1090        tracking::clear_state();
1091        {
1092            let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1093            let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1094            let lock_a3: LockDepMutex<i32, LevelA> = 0.into();
1095
1096            let _guard_a1 = lock_a1.lock();
1097            let _token = allow_subclass();
1098            let _guard_a2 = lock_a2.lock();
1099
1100            let _guard_a3 = lock_a3.lock();
1101        }
1102    }
1103
1104    #[test]
1105    fn test_raii_subclass_guard_multiple() {
1106        tracking::clear_state();
1107        {
1108            let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1109            let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1110            let lock_a3: LockDepMutex<i32, LevelA> = 0.into();
1111
1112            let _guard_a1 = lock_a1.lock();
1113            let _token1 = allow_subclass();
1114            let _guard_a2 = lock_a2.lock();
1115
1116            let _token2 = allow_subclass();
1117            let _guard_a3 = lock_a3.lock(); // Should succeed with subclass 2
1118        }
1119    }
1120
1121    #[test]
1122    #[should_panic(expected = "Invalid lock ordering cycle detected")]
1123    fn test_invalid_lock_ordering_cycle() {
1124        tracking::clear_state();
1125        {
1126            let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1127            let lock_b: LockDepMutex<i32, LevelB> = 0.into();
1128
1129            let _guard_b = lock_b.lock();
1130            let _guard_a = lock_a.lock(); // Should panic because B > A
1131        }
1132    }
1133
1134    #[test]
1135    #[should_panic(expected = "LockDep: Self-deadlock detected")]
1136    fn test_self_deadlock() {
1137        tracking::clear_state();
1138        {
1139            let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1140
1141            let _guard_a1 = lock_a.lock();
1142            let _guard_a2 = lock_a.lock();
1143        }
1144    }
1145
1146    #[test]
1147    #[should_panic(
1148        expected = "LockDep: Invalid lock ordering detected: acquired lock 'TerminalD' while holding lock 'TerminalC'"
1149    )]
1150    fn test_terminal_locks_self_deadlock() {
1151        tracking::clear_state();
1152        {
1153            let lock_c: LockDepMutex<i32, TerminalC> = 0.into();
1154            let lock_d: LockDepMutex<i32, TerminalD> = 0.into();
1155
1156            let _guard_c = lock_c.lock();
1157            let _guard_d = lock_d.lock();
1158        }
1159    }
1160
1161    #[test]
1162    fn test_subclass_drop_out_of_order() {
1163        tracking::clear_state();
1164        let lock_a1: LockDepMutex<i32, LevelA> = 0.into();
1165        let lock_a2: LockDepMutex<i32, LevelA> = 0.into();
1166        let lock_a3: LockDepMutex<i32, LevelA> = 0.into();
1167
1168        let _guard_a1 = lock_a1.lock();
1169        let _token1 = allow_subclass();
1170        let _guard_a2 = lock_a2.lock();
1171        let _token2 = allow_subclass();
1172        let _guard_a3 = lock_a3.lock();
1173        std::mem::drop(_token2);
1174        std::mem::drop(_guard_a2);
1175        std::mem::drop(_guard_a3);
1176        let _guard_a2 = lock_a2.lock();
1177    }
1178
1179    #[test]
1180    #[should_panic(expected = "LockDep: Attempted to drop a lock with active subclass tokens!")]
1181    fn test_drop_lock_with_active_tokens() {
1182        tracking::clear_state();
1183        let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1184        let guard = lock_a.lock();
1185        let _token = allow_subclass();
1186        std::mem::drop(guard);
1187    }
1188
1189    #[test]
1190    #[should_panic(
1191        expected = "Invalid lock ordering cycle detected: attempted to acquire 'LevelA' after 'LevelB'"
1192    )]
1193    fn test_panic_message_contains_names() {
1194        tracking::clear_state();
1195        let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1196        let lock_b: LockDepMutex<i32, LevelB> = 0.into();
1197
1198        let _guard_b = lock_b.lock();
1199        let _guard_a = lock_a.lock();
1200    }
1201
1202    #[test]
1203    #[should_panic(expected = "Invalid lock ordering cycle detected")]
1204    fn test_assert_lock_level_panic() {
1205        tracking::clear_state();
1206        let lock_b: LockDepMutex<i32, LevelB> = 0.into();
1207
1208        let _guard_b = lock_b.lock();
1209        // LevelA is before LevelB in the ordering.
1210        // So asserting LevelA after holding LevelB should panic!
1211        let _token = assert_lock_level::<LevelA>();
1212    }
1213
1214    #[test]
1215    fn test_ordered_lock() {
1216        tracking::clear_state();
1217        let lock1: LockDepMutex<i32, LevelA> = 1.into();
1218        let lock2: LockDepMutex<i32, LevelA> = 2.into();
1219
1220        {
1221            let (g1, g2) = ordered_lock(&lock1, &lock2);
1222            assert_eq!(*g1, 1);
1223            assert_eq!(*g2, 2);
1224        }
1225
1226        {
1227            let (g2, g1) = ordered_lock(&lock2, &lock1);
1228            assert_eq!(*g1, 1);
1229            assert_eq!(*g2, 2);
1230        }
1231    }
1232
1233    #[test]
1234    fn test_ordered_lock_vec() {
1235        tracking::clear_state();
1236        let l0: LockDepMutex<i32, LevelA> = 0.into();
1237        let l1: LockDepMutex<i32, LevelA> = 1.into();
1238        let l2: LockDepMutex<i32, LevelA> = 2.into();
1239
1240        {
1241            let guards = ordered_lock_vec(&[&l0, &l1, &l2]);
1242            assert_eq!(*guards[0], 0);
1243            assert_eq!(*guards[1], 1);
1244            assert_eq!(*guards[2], 2);
1245        }
1246
1247        {
1248            let guards = ordered_lock_vec(&[&l2, &l1, &l0]);
1249            assert_eq!(*guards[0], 2);
1250            assert_eq!(*guards[1], 1);
1251            assert_eq!(*guards[2], 0);
1252        }
1253    }
1254
1255    #[test]
1256    fn test_ordered_lock_vec_many_locks() {
1257        tracking::clear_state();
1258        let locks: Vec<LockDepMutex<i32, LevelA>> = (0..20).map(|i| i.into()).collect();
1259        let lock_refs: Vec<&LockDepMutex<i32, LevelA>> = locks.iter().collect();
1260
1261        let guards = ordered_lock_vec(&lock_refs);
1262        assert_eq!(guards.len(), 20);
1263        for i in 0..20 {
1264            assert_eq!(*guards[i], i as i32);
1265        }
1266    }
1267
1268    #[test]
1269    fn test_dynamic_lockdep_success() {
1270        tracking::clear_state();
1271        let l1 = DynamicLockDepMutex::new::<LevelA>(1);
1272        let l2 = DynamicLockDepMutex::new::<LevelB>(2);
1273        let _g1 = l1.lock();
1274        let _g2 = l2.lock();
1275    }
1276
1277    #[test]
1278    #[should_panic(expected = "Invalid lock ordering cycle detected")]
1279    fn test_dynamic_lockdep_failure() {
1280        tracking::clear_state();
1281        let l1 = DynamicLockDepMutex::new::<LevelA>(1);
1282        let l2 = DynamicLockDepMutex::new::<LevelB>(2);
1283        let _g2 = l2.lock();
1284        let _g1 = l1.lock();
1285    }
1286
1287    #[test]
1288    fn test_dynamic_lockdep_subclass() {
1289        tracking::clear_state();
1290        let l1 = DynamicLockDepMutex::new::<LevelA>(1);
1291        let l2 = DynamicLockDepMutex::new::<LevelA>(2);
1292        let _g1 = l1.lock();
1293        let _subclass = tracking::SubclassToken::new();
1294        let _g2 = l2.lock();
1295    }
1296
1297    #[test]
1298    fn test_try_lock() {
1299        tracking::clear_state();
1300        let l1: LockDepMutex<i32, LevelA> = 1.into();
1301        let g1 = l1.try_lock();
1302        assert!(g1.is_some());
1303        std::thread::scope(|s| {
1304            s.spawn(|| {
1305                assert!(l1.try_lock().is_none());
1306            });
1307        });
1308
1309        let l2: LockDepRwLock<i32, LevelB> = 2.into();
1310        let g2 = l2.try_read();
1311        assert!(g2.is_some());
1312        std::thread::scope(|s| {
1313            s.spawn(|| {
1314                assert!(l2.try_read().is_some());
1315            });
1316            s.spawn(|| {
1317                assert!(l2.try_write().is_none());
1318            });
1319        });
1320        std::mem::drop(g2);
1321
1322        let g4 = l2.try_write();
1323        assert!(g4.is_some());
1324        std::thread::scope(|s| {
1325            s.spawn(|| {
1326                assert!(l2.try_read().is_none());
1327            });
1328        });
1329    }
1330
1331    #[test]
1332    #[should_panic(expected = "Invalid lock ordering cycle detected")]
1333    fn test_try_lock_ordering_failure() {
1334        tracking::clear_state();
1335        let l1: LockDepMutex<i32, LevelA> = 1.into();
1336        let l2: LockDepMutex<i32, LevelB> = 2.into();
1337
1338        let _g2 = l2.try_lock();
1339        let _g1 = l1.try_lock(); // This should panic since we hold B and request A.
1340    }
1341
1342    #[test]
1343    fn test_unlocked_guard_tracking() {
1344        tracking::clear_state();
1345        let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1346        let lock_b: LockDepMutex<i32, LevelB> = 0.into();
1347
1348        let mut guard_b = lock_b.lock();
1349
1350        LockDepGuard::unlocked(&mut guard_b, || {
1351            // Because B is unlocked, we can now acquire A, which would normally panic
1352            // if we were still holding B (since A is before B).
1353            let _guard_a = lock_a.lock();
1354        });
1355    }
1356
1357    #[test]
1358    fn test_unlocked_guard_reacquire() {
1359        tracking::clear_state();
1360        let lock_a: LockDepMutex<i32, LevelA> = 0.into();
1361
1362        let mut guard_a = lock_a.lock();
1363        LockDepGuard::unlocked(&mut guard_a, || {
1364            // Because A is unlocked and popped from tracking, we can acquire it again.
1365            let _guard_a2 = lock_a.lock();
1366        });
1367    }
1368
1369    fn assert_rcu_droppable<T: fuchsia_rcu::RcuDroppable>() {}
1370
1371    #[test]
1372    fn test_rcu_droppable() {
1373        assert_rcu_droppable::<LevelA>();
1374        assert_rcu_droppable::<TerminalC>();
1375        assert_rcu_droppable::<DynamicLockDepMutex<i32>>();
1376        assert_rcu_droppable::<DynamicLockDepRwLock<i32>>();
1377        assert_rcu_droppable::<LockDepMutex<i32, LevelA>>();
1378        assert_rcu_droppable::<LockDepRwLock<i32, LevelA>>();
1379    }
1380}
1381
1382impl<T> fuchsia_sync::ResetDependencies for DynamicLockDepMutex<T> {
1383    #[inline(always)]
1384    unsafe fn reset_dependencies(&self) {
1385        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
1386        unsafe { fuchsia_sync::ResetDependencies::reset_dependencies(&self.inner) }
1387    }
1388}
1389
1390impl<T, L> fuchsia_sync::ResetDependencies for LockDepMutex<T, L> {
1391    #[inline(always)]
1392    unsafe fn reset_dependencies(&self) {
1393        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
1394        unsafe { fuchsia_sync::ResetDependencies::reset_dependencies(&self.inner) }
1395    }
1396}
1397
1398impl<T> fuchsia_sync::ResetDependencies for DynamicLockDepRwLock<T> {
1399    #[inline(always)]
1400    unsafe fn reset_dependencies(&self) {
1401        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
1402        unsafe { fuchsia_sync::ResetDependencies::reset_dependencies(&self.inner) }
1403    }
1404}
1405
1406impl<T, L> fuchsia_sync::ResetDependencies for LockDepRwLock<T, L> {
1407    #[inline(always)]
1408    unsafe fn reset_dependencies(&self) {
1409        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
1410        unsafe { fuchsia_sync::ResetDependencies::reset_dependencies(&self.inner) }
1411    }
1412}
1413
1414impl<T, L: crate::LockLevel> fuchsia_inspect_derive::Inspect for &LockDepMutex<T, L>
1415where
1416    for<'a> &'a mut T: fuchsia_inspect_derive::Inspect,
1417{
1418    fn iattach(
1419        self,
1420        parent: &fuchsia_inspect::Node,
1421        name: impl AsRef<str>,
1422    ) -> Result<(), fuchsia_inspect_derive::AttachError> {
1423        match self.try_lock() {
1424            Some(mut inner) => inner.iattach(parent, name),
1425            None => Err("could not get exclusive access to LockDepMutex".into()),
1426        }
1427    }
1428}