Skip to main content

fuchsia_rcu/
rcu_arc.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::rcu_ptr::RcuPtr;
6use crate::rcu_read_scope::RcuReadScope;
7use crate::state_machine::rcu_drop;
8use crate::subtle::rcu_ptr_upgrade;
9use std::sync::Arc;
10
11/// A version of [crate::RcuOptionArc] which does not require `T: RcuDroppable` in exchange for
12/// some extra atomic checking on reads.
13///
14/// RcuArc allows arbitrary types to be used with RCU by separating `Drop`ing the type,
15/// which may not be safe to do on the RCU advancer thread, and freeing the allocation for the
16/// type, which is safe to do, once an RCU grace period has elapsed.
17///
18/// We do the former by `Drop`ing the old `Arc<T>` synchronously on the calling thread when we
19/// do a replace. We do the latter by first placing a `Weak<T>` in the RCU callback queue to hold
20/// the memory alive (but with a potential strong count of zero) until an RCU grace period has
21/// elapsed.
22///
23/// Since the strong count of the memory may drop to zero even while a reader holds an
24/// RcuReadScope, readers must verify the memory is safe to read. Readers safely do this by using a
25/// Weak::upgrade, which serializes with a compare_exchange loop on the strong count.
26#[derive(Debug)]
27pub struct RcuArc<T: Send + Sync + 'static> {
28    ptr: RcuPtr<T>,
29}
30
31impl<T: Send + Sync + 'static> RcuArc<T> {
32    /// Create a new RcuArc from an `Option<Arc<T>>`.
33    pub fn new(data: impl Into<Option<Arc<T>>>) -> Self {
34        Self { ptr: RcuPtr::new(Self::into_ptr(data.into())) }
35    }
36
37    /// Read the contents of the RcuArc.
38    ///
39    /// Returns `None` if the wrapped Arc is `None` or is no longer valid.
40    pub fn upgrade(&self) -> Option<Arc<T>> {
41        let scope = RcuReadScope::new();
42        loop {
43            let ptr = self.ptr.read(&scope);
44            // If the pointer is null, we're done.
45            if ptr.is_null() {
46                return None;
47            }
48            // We could be racing with a call to [Self::update] here. If we get a non null pointer,
49            // but fail to upgrade the weak pointer, we simply retry. Because the writer updates
50            // `self.ptr` before dropping the old Arc, re-reading `self.ptr` will observe the new
51            // pointer (or null).
52
53            // SAFETY: `ptr` was created from an Arc::into_raw() or is null.
54            if let Some(arc) = unsafe { rcu_ptr_upgrade(ptr) } {
55                return Some(arc);
56            }
57        }
58    }
59
60    /// Write a new `Option<Arc<T>>` to the RcuArc.
61    ///
62    /// The old `Arc<T>` (if any) is dropped on the caller's thread, running `T::drop()`
63    /// synchronously if it was the last strong reference.
64    pub fn update(&self, data: impl Into<Option<Arc<T>>>) {
65        let ptr = Self::into_ptr(data.into());
66        // SAFETY: We pass a pointer obtained from `Self::into_ptr`.
67        unsafe { self.replace(ptr) };
68    }
69
70    /// Returns `true` if the RCU wrapper currently contains a value.
71    ///
72    /// Unlike `self.upgrade().is_some()`, this neither takes a transient strong reference nor
73    /// enters an `RcuReadScope`. A non-null pointer stored in the `RcuArc` always owns a strong
74    /// count, and the pointer is never dereferenced, so observing a non-null pointer is sufficient.
75    /// This guarantees that calling `is_some()` can never result in the caller running `T::drop()`,
76    /// which matters when `T::drop()` may acquire locks.
77    pub fn is_some(&self) -> bool {
78        !self.ptr.is_null()
79    }
80
81    /// Returns `true` if the RCU wrapper does not contain a value.
82    ///
83    /// See [`Self::is_some`] for why this does not upgrade.
84    pub fn is_none(&self) -> bool {
85        !self.is_some()
86    }
87
88    /// Extract the raw pointer from an `Option<Arc<T>>`.
89    fn into_ptr(data: Option<Arc<T>>) -> *mut T {
90        match data {
91            Some(arc) => Arc::into_raw(arc) as *mut T,
92            None => std::ptr::null_mut(),
93        }
94    }
95
96    /// Replace the pointer in the `RcuArc` with a new pointer.
97    ///
98    /// # Safety
99    ///
100    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
101    unsafe fn replace(&self, ptr: *mut T) {
102        let old_ptr = self.ptr.replace(ptr);
103        if !old_ptr.is_null() {
104            // SAFETY: The caller ensures the pointer is obtained from Self::into_ptr or
105            // std::ptr::null_mut.
106            let old_arc = unsafe { Arc::from_raw(old_ptr) };
107            let weak = Arc::downgrade(&old_arc);
108            drop(old_arc);
109            rcu_drop(weak);
110        }
111    }
112}
113
114impl<T: Send + Sync + 'static> Drop for RcuArc<T> {
115    fn drop(&mut self) {
116        // SAFETY: We can pass `std::ptr::null_mut`.
117        unsafe { self.replace(std::ptr::null_mut()) };
118    }
119}
120
121impl<T: Send + Sync + 'static> Clone for RcuArc<T> {
122    fn clone(&self) -> Self {
123        Self::new(self.upgrade())
124    }
125}
126
127impl<T: Send + Sync + 'static> From<Option<Arc<T>>> for RcuArc<T> {
128    fn from(data: Option<Arc<T>>) -> Self {
129        Self::new(data)
130    }
131}
132
133impl<T: Send + Sync + 'static> From<Arc<T>> for RcuArc<T> {
134    fn from(data: Arc<T>) -> Self {
135        Self::new(Some(data))
136    }
137}
138
139impl<T: Send + Sync + 'static> Default for RcuArc<T> {
140    fn default() -> Self {
141        Self::new(None)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::state_machine::rcu_run_callbacks;
149    use std::sync::atomic::{AtomicUsize, Ordering};
150
151    // A struct that intentionally does NOT implement RcuDroppable
152    struct DropCounter {
153        value: usize,
154        drops: Arc<AtomicUsize>,
155    }
156
157    impl DropCounter {
158        pub fn new(value: usize, drops: Arc<AtomicUsize>) -> Arc<Self> {
159            Arc::new(Self { value, drops })
160        }
161    }
162
163    impl Drop for DropCounter {
164        fn drop(&mut self) {
165            self.drops.fetch_add(1, Ordering::Relaxed);
166        }
167    }
168
169    #[test]
170    fn test_arc_update_and_synchronous_drop() {
171        // We should see the Drop handler trigger before rcu_synchronize.
172        let drops = Arc::new(AtomicUsize::new(0));
173        let arc = RcuArc::new(Some(DropCounter::new(42, drops.clone())));
174
175        assert!(arc.is_some());
176        assert_eq!(arc.upgrade().unwrap().value, 42);
177        assert_eq!(drops.load(Ordering::Relaxed), 0);
178
179        arc.update(Some(DropCounter::new(43, drops.clone())));
180        assert_eq!(arc.upgrade().unwrap().value, 43);
181        assert_eq!(drops.load(Ordering::Relaxed), 1, "Drop must execute synchronously on update");
182
183        arc.update(None);
184        assert!(arc.is_none());
185        assert_eq!(drops.load(Ordering::Relaxed), 2, "Drop must execute synchronously on reset");
186
187        rcu_run_callbacks();
188        assert_eq!(drops.load(Ordering::Relaxed), 2);
189    }
190
191    #[test]
192    fn test_arc_default() {
193        let arc = RcuArc::<DropCounter>::default();
194        assert!(arc.is_none());
195        assert!(arc.upgrade().is_none());
196    }
197
198    #[test]
199    fn test_arc_clone() {
200        let drops = Arc::new(AtomicUsize::new(0));
201        let arc1 = RcuArc::new(Some(DropCounter::new(100, drops.clone())));
202        let arc2 = arc1.clone();
203
204        assert_eq!(arc1.upgrade().unwrap().value, 100);
205        assert_eq!(arc2.upgrade().unwrap().value, 100);
206
207        drop(arc1);
208        // arc2 still holds a strong reference to the value.
209        assert_eq!(drops.load(Ordering::Relaxed), 0);
210
211        drop(arc2);
212        assert_eq!(drops.load(Ordering::Relaxed), 1);
213    }
214}