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    pub fn is_some(&self) -> bool {
72        self.upgrade().is_some()
73    }
74
75    /// Returns `true` if the RCU wrapper does not contain a value.
76    pub fn is_none(&self) -> bool {
77        self.upgrade().is_none()
78    }
79
80    /// Extract the raw pointer from an `Option<Arc<T>>`.
81    fn into_ptr(data: Option<Arc<T>>) -> *mut T {
82        match data {
83            Some(arc) => Arc::into_raw(arc) as *mut T,
84            None => std::ptr::null_mut(),
85        }
86    }
87
88    /// Replace the pointer in the `RcuArc` with a new pointer.
89    ///
90    /// # Safety
91    ///
92    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
93    unsafe fn replace(&self, ptr: *mut T) {
94        let old_ptr = self.ptr.replace(ptr);
95        if !old_ptr.is_null() {
96            // SAFETY: The caller ensures the pointer is obtained from Self::into_ptr or
97            // std::ptr::null_mut.
98            let old_arc = unsafe { Arc::from_raw(old_ptr) };
99            let weak = Arc::downgrade(&old_arc);
100            drop(old_arc);
101            rcu_drop(weak);
102        }
103    }
104}
105
106impl<T: Send + Sync + 'static> Drop for RcuArc<T> {
107    fn drop(&mut self) {
108        // SAFETY: We can pass `std::ptr::null_mut`.
109        unsafe { self.replace(std::ptr::null_mut()) };
110    }
111}
112
113impl<T: Send + Sync + 'static> Clone for RcuArc<T> {
114    fn clone(&self) -> Self {
115        Self::new(self.upgrade())
116    }
117}
118
119impl<T: Send + Sync + 'static> From<Option<Arc<T>>> for RcuArc<T> {
120    fn from(data: Option<Arc<T>>) -> Self {
121        Self::new(data)
122    }
123}
124
125impl<T: Send + Sync + 'static> From<Arc<T>> for RcuArc<T> {
126    fn from(data: Arc<T>) -> Self {
127        Self::new(Some(data))
128    }
129}
130
131impl<T: Send + Sync + 'static> Default for RcuArc<T> {
132    fn default() -> Self {
133        Self::new(None)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::state_machine::rcu_run_callbacks;
141    use std::sync::atomic::{AtomicUsize, Ordering};
142
143    // A struct that intentionally does NOT implement RcuDroppable
144    struct DropCounter {
145        value: usize,
146        drops: Arc<AtomicUsize>,
147    }
148
149    impl DropCounter {
150        pub fn new(value: usize, drops: Arc<AtomicUsize>) -> Arc<Self> {
151            Arc::new(Self { value, drops })
152        }
153    }
154
155    impl Drop for DropCounter {
156        fn drop(&mut self) {
157            self.drops.fetch_add(1, Ordering::Relaxed);
158        }
159    }
160
161    #[test]
162    fn test_arc_update_and_synchronous_drop() {
163        // We should see the Drop handler trigger before rcu_synchronize.
164        let drops = Arc::new(AtomicUsize::new(0));
165        let arc = RcuArc::new(Some(DropCounter::new(42, drops.clone())));
166
167        assert!(arc.is_some());
168        assert_eq!(arc.upgrade().unwrap().value, 42);
169        assert_eq!(drops.load(Ordering::Relaxed), 0);
170
171        arc.update(Some(DropCounter::new(43, drops.clone())));
172        assert_eq!(arc.upgrade().unwrap().value, 43);
173        assert_eq!(drops.load(Ordering::Relaxed), 1, "Drop must execute synchronously on update");
174
175        arc.update(None);
176        assert!(arc.is_none());
177        assert_eq!(drops.load(Ordering::Relaxed), 2, "Drop must execute synchronously on reset");
178
179        rcu_run_callbacks();
180        assert_eq!(drops.load(Ordering::Relaxed), 2);
181    }
182
183    #[test]
184    fn test_arc_default() {
185        let arc = RcuArc::<DropCounter>::default();
186        assert!(arc.is_none());
187        assert!(arc.upgrade().is_none());
188    }
189
190    #[test]
191    fn test_arc_clone() {
192        let drops = Arc::new(AtomicUsize::new(0));
193        let arc1 = RcuArc::new(Some(DropCounter::new(100, drops.clone())));
194        let arc2 = arc1.clone();
195
196        assert_eq!(arc1.upgrade().unwrap().value, 100);
197        assert_eq!(arc2.upgrade().unwrap().value, 100);
198
199        drop(arc1);
200        // arc2 still holds a strong reference to the value.
201        assert_eq!(drops.load(Ordering::Relaxed), 0);
202
203        drop(arc2);
204        assert_eq!(drops.load(Ordering::Relaxed), 1);
205    }
206}