Skip to main content

starnix_sync/
lock_dep_mutex.rs

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