Skip to main content

starnix_sync/
rcu_lock.rs

1// Copyright 2026 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::{DynamicLockDepMutex, LockDepGuard, LockLevel, RcuLockLevel};
6use fuchsia_rcu::{
7    RcuArc, RcuBox, RcuDroppable, RcuDroppableArc, RcuOptionArc, RcuOptionBox, RcuReadGuard,
8    RcuReadScope, RcuWeak,
9};
10use fuchsia_rcu_collections::rcu_array::RcuArray;
11use std::cell::Cell;
12use std::marker::PhantomData;
13use std::ops::Deref;
14use std::sync::{Arc, Weak};
15
16/// A synchronization wrapper that combines an RCU primitive `T` with a [`DynamicLockDepMutex`] to
17/// serialize updates while permitting lock-free RCU reads, without using types for lock levels.
18///
19/// Readers access the underlying RCU value without acquiring the mutex via the read methods on
20/// `DynamicRcuLock`. Writers must acquire the mutex via [`DynamicRcuLock::lock`] or
21/// [`DynamicRcuLock::try_lock`] to obtain an [`RcuLockGuard`], which grants exclusive writer
22/// access to `T`.
23#[derive(RcuDroppable)]
24pub struct DynamicRcuLock<T> {
25    rcu: T,
26    lock: DynamicLockDepMutex<()>,
27}
28
29/// A synchronization wrapper that combines an RCU primitive `T` with a lock at level `L` to
30/// serialize updates while permitting lock-free RCU reads.
31///
32/// Readers access the underlying RCU value without acquiring the mutex via the read methods on
33/// [`DynamicRcuLock`]. Writers must acquire the mutex via [`DynamicRcuLock::lock`] or
34/// [`DynamicRcuLock::try_lock`] to obtain an [`RcuLockGuard`], which grants exclusive writer
35/// access to `T`.
36#[derive(RcuDroppable)]
37pub struct RcuLock<T, L = RcuLockLevel> {
38    inner: DynamicRcuLock<T>,
39    _level: PhantomData<L>,
40}
41
42/// A guard that holds the update lock for an [`RcuLock`] or [`DynamicRcuLock`] and provides
43/// exclusive writer access to the underlying RCU primitive `T`.
44pub struct RcuLockGuard<'a, T> {
45    rcu: &'a T,
46    _guard: LockDepGuard<'a, ()>,
47    _not_sync: PhantomData<Cell<()>>,
48}
49
50impl<T> DynamicRcuLock<T> {
51    /// Creates a new `DynamicRcuLock` wrapping `rcu` at lock level `L`.
52    pub const fn new<L: LockLevel>(rcu: T) -> Self {
53        Self { rcu, lock: DynamicLockDepMutex::new::<L>(()) }
54    }
55
56    /// Acquires the update lock and returns a guard that allows updating the RCU cell.
57    #[inline(always)]
58    #[track_caller]
59    pub fn lock(&self) -> RcuLockGuard<'_, T> {
60        RcuLockGuard { rcu: &self.rcu, _guard: self.lock.lock(), _not_sync: PhantomData }
61    }
62
63    /// Attempts to acquire the update lock without blocking.
64    #[inline(always)]
65    #[track_caller]
66    pub fn try_lock(&self) -> Option<RcuLockGuard<'_, T>> {
67        Some(RcuLockGuard { rcu: &self.rcu, _guard: self.lock.try_lock()?, _not_sync: PhantomData })
68    }
69}
70
71impl<T: std::fmt::Debug> std::fmt::Debug for DynamicRcuLock<T> {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.debug_struct("DynamicRcuLock").field("rcu", &self.rcu).field("lock", &self.lock).finish()
74    }
75}
76
77impl<T> crate::MutexLike for DynamicRcuLock<T> {
78    type Guard<'a>
79        = RcuLockGuard<'a, T>
80    where
81        T: 'a;
82    type Context = <DynamicLockDepMutex<()> as crate::MutexLike>::Context;
83
84    #[inline(always)]
85    fn context() -> Self::Context {
86        <DynamicLockDepMutex<()> as crate::MutexLike>::context()
87    }
88
89    #[inline(always)]
90    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
91        RcuLockGuard {
92            rcu: &self.rcu,
93            _guard: <DynamicLockDepMutex<()> as crate::MutexLike>::lock(&self.lock, context),
94            _not_sync: PhantomData,
95        }
96    }
97}
98
99impl<T> fuchsia_sync::ResetDependencies for DynamicRcuLock<T> {
100    #[inline(always)]
101    unsafe fn reset_dependencies(&self) {
102        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
103        unsafe { fuchsia_sync::ResetDependencies::reset_dependencies(&self.lock) }
104    }
105}
106
107impl<T, L: LockLevel> RcuLock<T, L> {
108    /// Creates a new `RcuLock` wrapping `rcu`.
109    pub const fn new(rcu: T) -> Self {
110        Self { inner: DynamicRcuLock::new::<L>(rcu), _level: PhantomData }
111    }
112}
113
114impl<T, L> Deref for RcuLock<T, L> {
115    type Target = DynamicRcuLock<T>;
116
117    #[inline(always)]
118    fn deref(&self) -> &Self::Target {
119        &self.inner
120    }
121}
122
123impl<T: Default, L: LockLevel> Default for RcuLock<T, L> {
124    fn default() -> Self {
125        Self::new(T::default())
126    }
127}
128
129impl<T, L: LockLevel> From<T> for RcuLock<T, L> {
130    fn from(rcu: T) -> Self {
131        Self::new(rcu)
132    }
133}
134
135impl<T: std::fmt::Debug, L> std::fmt::Debug for RcuLock<T, L> {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        f.debug_struct("RcuLock")
138            .field("rcu", &self.inner.rcu)
139            .field("lock", &self.inner.lock)
140            .field("level", &std::any::type_name::<L>())
141            .finish()
142    }
143}
144
145impl<T, L> crate::MutexLike for RcuLock<T, L> {
146    type Guard<'a>
147        = RcuLockGuard<'a, T>
148    where
149        T: 'a,
150        L: 'a;
151    type Context = <DynamicRcuLock<T> as crate::MutexLike>::Context;
152
153    #[inline(always)]
154    fn context() -> Self::Context {
155        <DynamicRcuLock<T> as crate::MutexLike>::context()
156    }
157
158    #[inline(always)]
159    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
160        <DynamicRcuLock<T> as crate::MutexLike>::lock(&self.inner, context)
161    }
162}
163
164impl<T, L> fuchsia_sync::ResetDependencies for RcuLock<T, L> {
165    #[inline(always)]
166    unsafe fn reset_dependencies(&self) {
167        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
168        unsafe { fuchsia_sync::ResetDependencies::reset_dependencies(&self.inner) }
169    }
170}
171
172impl<T> Deref for RcuLockGuard<'_, T> {
173    type Target = T;
174
175    #[inline(always)]
176    fn deref(&self) -> &Self::Target {
177        self.rcu
178    }
179}
180
181impl<T: std::fmt::Debug> std::fmt::Debug for RcuLockGuard<'_, T> {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        f.debug_struct("RcuLockGuard").field("rcu", &self.rcu).finish()
184    }
185}
186
187impl<T: Send + Sync + 'static> DynamicRcuLock<RcuArc<T>> {
188    /// Reads the contents of the underlying [`RcuArc`] without acquiring the update lock.
189    ///
190    /// Returns `None` if the wrapped `Arc` is `None` or is no longer valid.
191    pub fn upgrade(&self) -> Option<Arc<T>> {
192        self.rcu.upgrade()
193    }
194
195    /// Returns `true` if the underlying [`RcuArc`] currently contains a value.
196    pub fn is_some(&self) -> bool {
197        self.rcu.is_some()
198    }
199
200    /// Returns `true` if the underlying [`RcuArc`] does not contain a value.
201    pub fn is_none(&self) -> bool {
202        self.rcu.is_none()
203    }
204}
205
206impl<T: RcuDroppable + Sync> DynamicRcuLock<RcuArray<T>> {
207    /// Returns a reference to the element at `index`, or `None` if `index` is out of bounds.
208    pub fn get<'a>(&self, scope: &'a RcuReadScope, index: usize) -> Option<&'a T> {
209        self.rcu.get(scope, index)
210    }
211
212    /// Returns a slice containing the entire array bound to `scope`.
213    pub fn as_slice<'a>(&self, scope: &'a RcuReadScope) -> &'a [T] {
214        self.rcu.as_slice(scope)
215    }
216}
217
218impl<T: RcuDroppable + Sync> RcuLockGuard<'_, RcuArray<T>> {
219    /// Ensures that the array has at least `requested_size` elements, filling with
220    /// `T::default()` if the array needs to be grown.
221    pub fn ensure_at_least(&self, requested_size: usize)
222    where
223        T: Clone + Default,
224    {
225        // SAFETY: `RcuLockGuard` holds the update lock and is `!Sync`, guaranteeing that no other
226        // writer can concurrently modify the underlying `RcuArray`.
227        unsafe { self.rcu.ensure_at_least(requested_size) };
228    }
229}
230
231impl<T: RcuDroppable + Sync> DynamicRcuLock<RcuBox<T>> {
232    /// Reads the value of the wrapped [`RcuBox`] without acquiring the update lock.
233    pub fn read(&self) -> RcuReadGuard<T> {
234        self.rcu.read()
235    }
236
237    /// Returns a reference to the value of the wrapped [`RcuBox`] bound to `scope`.
238    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> &'a T {
239        self.rcu.as_ref(scope)
240    }
241
242    /// Returns a clone of the value of the wrapped [`RcuBox`].
243    pub fn cloned(&self) -> T
244    where
245        T: Clone,
246    {
247        self.rcu.cloned()
248    }
249}
250
251impl<T: RcuDroppable + Sync> DynamicRcuLock<RcuDroppableArc<T>> {
252    /// Reads the value of the wrapped [`RcuDroppableArc`] without acquiring the update lock.
253    pub fn read(&self) -> RcuReadGuard<T> {
254        self.rcu.read()
255    }
256
257    /// Returns a reference to the value of the wrapped [`RcuDroppableArc`] bound to `scope`.
258    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> &'a T {
259        self.rcu.as_ref(scope)
260    }
261
262    /// Creates a new `Arc<T>` referencing the same object as the underlying [`RcuDroppableArc`].
263    pub fn to_arc(&self) -> Arc<T> {
264        self.rcu.to_arc()
265    }
266}
267
268impl<T: RcuDroppable + Sync> DynamicRcuLock<RcuOptionArc<T>> {
269    /// Reads the value of the wrapped [`RcuOptionArc`] without acquiring the update lock.
270    pub fn read(&self) -> Option<RcuReadGuard<T>> {
271        self.rcu.read()
272    }
273
274    /// Returns a reference to the value of the wrapped [`RcuOptionArc`] bound to `scope`.
275    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> Option<&'a T> {
276        self.rcu.as_ref(scope)
277    }
278
279    /// Creates a new `Option<Arc<T>>` referencing the same object as the underlying
280    /// [`RcuOptionArc`].
281    pub fn to_option_arc(&self) -> Option<Arc<T>> {
282        self.rcu.to_option_arc()
283    }
284}
285
286impl<T: RcuDroppable + Sync> DynamicRcuLock<RcuOptionBox<T>> {
287    /// Reads the value of the wrapped [`RcuOptionBox`] without acquiring the update lock.
288    pub fn read(&self) -> Option<RcuReadGuard<T>> {
289        self.rcu.read()
290    }
291
292    /// Returns a reference to the value of the wrapped [`RcuOptionBox`] bound to `scope`.
293    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> Option<&'a T> {
294        self.rcu.as_ref(scope)
295    }
296
297    /// Returns `true` if the underlying [`RcuOptionBox`] currently contains a value.
298    pub fn is_some(&self, scope: &RcuReadScope) -> bool {
299        self.rcu.is_some(scope)
300    }
301
302    /// Returns `true` if the underlying [`RcuOptionBox`] does not contain a value.
303    pub fn is_none(&self, scope: &RcuReadScope) -> bool {
304        self.rcu.is_none(scope)
305    }
306
307    /// Returns a clone of the value of the wrapped [`RcuOptionBox`].
308    pub fn cloned(&self) -> Option<T>
309    where
310        T: Clone,
311    {
312        self.rcu.cloned()
313    }
314}
315
316impl<T: Send + Sync + 'static> DynamicRcuLock<RcuWeak<T>> {
317    /// Attempts to upgrade the wrapped [`Weak`] pointer to an [`Arc`] without acquiring the update
318    /// lock.
319    pub fn upgrade(&self) -> Option<Arc<T>> {
320        self.rcu.upgrade()
321    }
322
323    /// Creates a new [`Weak`] pointer to the object referenced by the underlying [`RcuWeak`].
324    pub fn to_weak(&self) -> Weak<T> {
325        self.rcu.to_weak()
326    }
327
328    /// Returns `true` if the wrapped weak pointer points to the same allocation as `other`.
329    pub fn ptr_eq(&self, other: &Weak<T>) -> bool {
330        self.rcu.ptr_eq(other)
331    }
332
333    /// Returns the number of strong (`Arc`) pointers pointing to the allocation.
334    pub fn strong_count(&self, scope: &RcuReadScope) -> usize {
335        self.rcu.strong_count(scope)
336    }
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342    use crate::{LockDepMutex, lock_ordering, ordered_lock, ordered_lock_vec};
343    use fuchsia_rcu::rcu_run_callbacks;
344    use fuchsia_sync::ResetDependencies;
345    use std::sync::atomic::{AtomicUsize, Ordering};
346
347    lock_ordering! {
348        Unlocked => LevelA,
349        LevelA => LevelB,
350    }
351
352    struct DropCounter {
353        value: usize,
354        drops: Arc<AtomicUsize>,
355    }
356
357    impl DropCounter {
358        fn new(value: usize, drops: Arc<AtomicUsize>) -> Arc<Self> {
359            Arc::new(Self { value, drops })
360        }
361    }
362
363    impl Drop for DropCounter {
364        fn drop(&mut self) {
365            self.drops.fetch_add(1, Ordering::Relaxed);
366        }
367    }
368
369    #[::fuchsia::test]
370    fn test_rcu_lock_arc() {
371        let drops = Arc::new(AtomicUsize::new(0));
372        let cell: RcuLock<RcuArc<DropCounter>, LevelA> = RcuLock::default();
373
374        assert!(cell.is_none());
375        assert!(!cell.is_some());
376        assert!(cell.upgrade().is_none());
377
378        cell.lock().update(Some(DropCounter::new(42, drops.clone())));
379        assert!(cell.is_some());
380        assert!(!cell.is_none());
381        assert_eq!(cell.upgrade().unwrap().value, 42);
382        assert_eq!(drops.load(Ordering::Relaxed), 0);
383
384        {
385            let guard = cell.lock();
386            assert!(guard.is_some());
387            assert!(!guard.is_none());
388            assert_eq!(guard.upgrade().unwrap().value, 42);
389            guard.update(Some(DropCounter::new(99, drops.clone())));
390        }
391        assert_eq!(cell.upgrade().unwrap().value, 99);
392        assert_eq!(drops.load(Ordering::Relaxed), 1);
393
394        cell.lock().update(None);
395        assert!(cell.is_none());
396        assert_eq!(drops.load(Ordering::Relaxed), 2);
397
398        rcu_run_callbacks();
399    }
400
401    #[::fuchsia::test]
402    fn test_rcu_lock_array() {
403        let array: RcuLock<RcuArray<u32>> = RcuLock::default();
404        {
405            let scope = RcuReadScope::new();
406            assert!(array.as_slice(&scope).is_empty());
407            assert_eq!(array.get(&scope, 0), None);
408        }
409
410        array.lock().update(vec![10, 20, 30]);
411        {
412            let scope = RcuReadScope::new();
413            assert_eq!(array.as_slice(&scope), &[10, 20, 30]);
414            assert_eq!(array.get(&scope, 1), Some(&20));
415            assert_eq!(array.get(&scope, 3), None);
416        }
417
418        {
419            let guard = array.lock();
420            guard.ensure_at_least(5);
421            let scope = RcuReadScope::new();
422            assert!(guard.as_slice(&scope).len() >= 5);
423            assert_eq!(&guard.as_slice(&scope)[..3], &[10, 20, 30]);
424            assert_eq!(guard.get(&scope, 2), Some(&30));
425        }
426
427        rcu_run_callbacks();
428    }
429
430    #[::fuchsia::test]
431    fn test_rcu_lock_box_and_options() {
432        let boxed: RcuLock<RcuBox<u32>> = RcuLock::new(RcuBox::new(1));
433        assert_eq!(*boxed.read(), 1);
434        assert_eq!(boxed.cloned(), 1);
435        {
436            let guard = boxed.lock();
437            assert_eq!(*guard.read(), 1);
438            assert_eq!(guard.cloned(), 1);
439            guard.update(2);
440            let scope = RcuReadScope::new();
441            assert_eq!(guard.as_ref(&scope), &2);
442            assert_eq!(boxed.as_ref(&scope), &2);
443        }
444
445        let droppable_arc: RcuLock<RcuDroppableArc<u32>> =
446            RcuLock::from(RcuDroppableArc::new(5.into()));
447        assert_eq!(*droppable_arc.read(), 5);
448        assert_eq!(*droppable_arc.to_arc(), 5);
449        {
450            let scope = RcuReadScope::new();
451            assert_eq!(droppable_arc.as_ref(&scope), &5);
452            let guard = droppable_arc.lock();
453            assert_eq!(*guard.read(), 5);
454            assert_eq!(*guard.to_arc(), 5);
455            assert_eq!(guard.as_ref(&scope), &5);
456            let old = guard.update_swap(&scope, Arc::new(6));
457            assert_eq!(*old, 5);
458            assert_eq!(droppable_arc.as_ref(&scope), &6);
459            guard.update(Arc::new(7));
460            assert_eq!(droppable_arc.as_ref(&scope), &7);
461        }
462
463        let option_arc: RcuLock<RcuOptionArc<u32>> = RcuLock::default();
464        assert!(option_arc.read().is_none());
465        {
466            let guard = option_arc.lock();
467            assert!(guard.read().is_none());
468            assert!(guard.to_option_arc().is_none());
469            guard.update(Some(Arc::new(7)));
470            assert_eq!(*guard.to_option_arc().unwrap(), 7);
471            let scope = RcuReadScope::new();
472            assert_eq!(guard.as_ref(&scope), Some(&7));
473            assert_eq!(option_arc.as_ref(&scope), Some(&7));
474        }
475        assert_eq!(*option_arc.to_option_arc().unwrap(), 7);
476
477        let option_box: RcuLock<RcuOptionBox<u32>> = RcuLock::default();
478        assert!(option_box.read().is_none());
479        {
480            let scope = RcuReadScope::new();
481            assert!(option_box.is_none(&scope));
482            assert!(!option_box.is_some(&scope));
483        }
484        {
485            let guard = option_box.lock();
486            let scope = RcuReadScope::new();
487            assert!(guard.read().is_none());
488            assert!(guard.is_none(&scope));
489            assert!(!guard.is_some(&scope));
490            guard.update(Some(8));
491            assert_eq!(guard.cloned(), Some(8));
492            assert!(guard.is_some(&scope));
493            assert!(!guard.is_none(&scope));
494            assert_eq!(guard.as_ref(&scope), Some(&8));
495        }
496        assert_eq!(option_box.cloned(), Some(8));
497        {
498            let scope = RcuReadScope::new();
499            assert!(option_box.is_some(&scope));
500            assert!(!option_box.is_none(&scope));
501            assert_eq!(option_box.as_ref(&scope), Some(&8));
502        }
503
504        let target = Arc::new(11u32);
505        let weak_target = Arc::downgrade(&target);
506        let rcu_weak: RcuLock<RcuWeak<u32>> = RcuLock::default();
507        assert!(rcu_weak.upgrade().is_none());
508        {
509            let guard = rcu_weak.lock();
510            assert!(guard.upgrade().is_none());
511            guard.update(weak_target.clone());
512            assert_eq!(*guard.upgrade().unwrap(), 11);
513            assert!(guard.ptr_eq(&weak_target));
514            assert!(guard.to_weak().upgrade().is_some());
515            let scope = RcuReadScope::new();
516            assert_eq!(guard.strong_count(&scope), 1);
517        }
518        assert_eq!(*rcu_weak.upgrade().unwrap(), 11);
519        assert!(rcu_weak.ptr_eq(&weak_target));
520        assert!(rcu_weak.to_weak().upgrade().is_some());
521        {
522            let scope = RcuReadScope::new();
523            assert_eq!(rcu_weak.strong_count(&scope), 1);
524        }
525
526        rcu_run_callbacks();
527    }
528
529    #[::fuchsia::test]
530    fn test_rcu_lock_conversions_and_accessors() {
531        let cell: RcuLock<RcuBox<u32>, LevelA> = RcuBox::new(20).into();
532        assert_eq!(cell.cloned(), 20);
533
534        assert!(!format!("{cell:?}").is_empty());
535        {
536            let guard = cell.lock();
537            assert!(!format!("{guard:?}").is_empty());
538        }
539
540        // SAFETY: No locks are held while resetting dependencies on `cell`.
541        unsafe {
542            cell.reset_dependencies();
543        }
544        rcu_run_callbacks();
545    }
546
547    #[::fuchsia::test]
548    fn test_rcu_lock_try_lock_and_ordered_lock() {
549        let cell1: RcuLock<RcuArc<u32>, LevelA> = RcuLock::default();
550        let cell2: RcuLock<RcuArc<u32>, LevelA> = RcuLock::default();
551
552        let guard = cell1.try_lock().expect("try_lock should succeed when unlocked");
553        std::thread::scope(|scope| {
554            scope.spawn(|| {
555                assert!(cell1.try_lock().is_none());
556            });
557        });
558        drop(guard);
559
560        let (guard1, guard2) = ordered_lock(&cell1, &cell2);
561        guard1.update(Some(Arc::new(1)));
562        guard2.update(Some(Arc::new(2)));
563        drop((guard1, guard2));
564
565        let (guard2, guard1) = ordered_lock(&cell2, &cell1);
566        guard1.update(Some(Arc::new(3)));
567        guard2.update(Some(Arc::new(4)));
568        drop((guard2, guard1));
569
570        let guards = ordered_lock_vec(&[&cell2, &cell1]);
571        guards[0].update(Some(Arc::new(20)));
572        guards[1].update(Some(Arc::new(10)));
573        drop(guards);
574
575        assert_eq!(*cell1.upgrade().unwrap(), 10);
576        assert_eq!(*cell2.upgrade().unwrap(), 20);
577        rcu_run_callbacks();
578    }
579
580    #[::fuchsia::test]
581    fn test_rcu_lock_while_holding_outer_lock() {
582        let outer: LockDepMutex<u32, LevelA> = LockDepMutex::new(1);
583        let inner_rcu: RcuLock<RcuArray<u32>> = RcuLock::default();
584
585        let _outer_guard = outer.lock();
586        inner_rcu.lock().update(vec![1, 2, 3]);
587
588        let scope = RcuReadScope::new();
589        assert_eq!(inner_rcu.as_slice(&scope), &[1, 2, 3]);
590        drop(scope);
591        drop(_outer_guard);
592        rcu_run_callbacks();
593    }
594
595    fn assert_rcu_droppable<T: RcuDroppable>() {}
596
597    #[::fuchsia::test]
598    fn test_rcu_lock_droppable() {
599        assert_rcu_droppable::<DynamicRcuLock<RcuArray<u32>>>();
600        assert_rcu_droppable::<RcuLock<RcuArray<u32>>>();
601        assert_rcu_droppable::<RcuLock<RcuBox<u32>, LevelA>>();
602        assert_rcu_droppable::<RcuLock<RcuDroppableArc<u32>, LevelA>>();
603        assert_rcu_droppable::<RcuLock<RcuOptionArc<u32>, LevelA>>();
604        assert_rcu_droppable::<RcuLock<RcuOptionBox<u32>, LevelA>>();
605    }
606
607    #[::fuchsia::test]
608    fn test_dynamic_rcu_lock() {
609        let dyn_cell = DynamicRcuLock::new::<LevelA>(RcuArc::new(Some(Arc::new(10u32))));
610        assert!(dyn_cell.is_some());
611        assert_eq!(*dyn_cell.upgrade().unwrap(), 10);
612
613        assert!(dyn_cell.try_lock().is_some());
614        dyn_cell.lock().update(Some(Arc::new(30)));
615        assert_eq!(*dyn_cell.upgrade().unwrap(), 30);
616
617        let typed_cell: RcuLock<RcuArc<u32>, LevelA> = RcuLock::new(RcuArc::default());
618        let dyn_ref: &DynamicRcuLock<RcuArc<u32>> = &typed_cell;
619        let (g1, g2) = ordered_lock(&dyn_cell, dyn_ref);
620        g1.update(Some(Arc::new(40)));
621        g2.update(Some(Arc::new(50)));
622        drop((g1, g2));
623
624        assert_eq!(*dyn_cell.upgrade().unwrap(), 40);
625        assert_eq!(*typed_cell.upgrade().unwrap(), 50);
626        assert!(!format!("{dyn_cell:?}").is_empty());
627
628        // SAFETY: No locks are held while resetting dependencies on `dyn_cell`.
629        unsafe {
630            dyn_cell.reset_dependencies();
631        }
632        rcu_run_callbacks();
633    }
634
635    #[cfg(feature = "detect_lock_dep_cycles")]
636    #[test]
637    #[should_panic(expected = "Invalid lock ordering cycle detected")]
638    fn test_dynamic_rcu_lock_ordering_cycle_panics() {
639        let dyn_a = DynamicRcuLock::new::<LevelA>(RcuArc::<u32>::default());
640        let dyn_b = DynamicRcuLock::new::<LevelB>(RcuArc::<u32>::default());
641
642        let _guard_b = dyn_b.lock();
643        let _guard_a = dyn_a.lock();
644    }
645
646    #[cfg(feature = "detect_lock_dep_cycles")]
647    #[test]
648    #[should_panic(expected = "Invalid lock ordering cycle detected")]
649    fn test_rcu_lock_ordering_cycle_panics() {
650        let rcu_a: RcuLock<RcuArc<u32>, LevelA> = RcuLock::default();
651        let mutex_b: LockDepMutex<u32, LevelB> = LockDepMutex::new(0);
652
653        let _guard_b = mutex_b.lock();
654        let _guard_a = rcu_a.lock();
655    }
656
657    #[cfg(feature = "detect_lock_dep_cycles")]
658    #[test]
659    #[should_panic(expected = "LockDep: Self-deadlock detected")]
660    fn test_rcu_lock_self_deadlock_panics() {
661        let rcu_a: RcuLock<RcuArc<u32>, LevelA> = RcuLock::default();
662
663        let _guard1 = rcu_a.lock();
664        let _guard2 = rcu_a.lock();
665    }
666
667    #[cfg(feature = "detect_lock_dep_cycles")]
668    #[test]
669    #[should_panic(expected = "Invalid lock ordering cycle detected")]
670    fn test_rcu_lock_try_lock_ordering_cycle_panics() {
671        let rcu_a: RcuLock<RcuArc<u32>, LevelA> = RcuLock::default();
672        let mutex_b: LockDepMutex<u32, LevelB> = LockDepMutex::new(0);
673
674        let _guard_b = mutex_b.try_lock();
675        let _guard_a = rcu_a.try_lock();
676    }
677}