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