Skip to main content

fuchsia_rcu/
rcu_droppable_arc.rs

1// Copyright 2025 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, RcuReadGuard};
6use crate::rcu_read_scope::RcuReadScope;
7use crate::state_machine::rcu_drop;
8use crate::subtle::RcuPtrRef;
9use std::sync::Arc;
10
11use crate::rcu_droppable::RcuDroppable;
12
13/// An RCU (Read-Copy-Update) wrapper around an `Arc` for types implementing [`RcuDroppable`].
14///
15/// The Arc can be dereferenced from multiple threads concurrently without blocking.
16/// When the Arc is replaced, reads may continue to see the old Arc pointer for some period of time.
17#[derive(Debug)]
18pub struct RcuDroppableArc<T: RcuDroppable + Sync> {
19    ptr: RcuPtr<T>,
20}
21
22impl<T: RcuDroppable + Sync> RcuDroppableArc<T> {
23    /// Create a new RCU wrapper around an `Arc`.
24    pub fn new(data: Arc<T>) -> Self {
25        Self { ptr: RcuPtr::new(Self::into_ptr(data)) }
26    }
27
28    /// Read the value of the wrapped Arc.
29    ///
30    /// The object referenced by the RCU Arc will remain valid until the `RcuReadGuard` is dropped.
31    /// However, another thread running concurrently might see a different value for the object.
32    pub fn read(&self) -> RcuReadGuard<T> {
33        self.ptr.get()
34    }
35
36    /// Returns a reference to the value of the wrapped Arc.
37    ///
38    /// The object referenced by the RCU Arc will remain valid until the `RcuReadScope` is dropped.
39    /// However, another thread running concurrently might see a different value for the object.
40    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> &'a T {
41        self.ptr.read(scope).as_ref().unwrap()
42    }
43
44    /// Write a new Arc to the RCU wrapper.
45    ///
46    /// Concurrent readers may continue to see the old Arc pointer until the RCU state machine has
47    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
48    pub fn update(&self, data: Arc<T>) {
49        let ptr = Self::into_ptr(data);
50        // SAFETY: We can pass `Self::into_ptr` to `Self::replace`.
51        unsafe { self.replace(ptr) };
52    }
53
54    /// Write a new Arc to the RCU wrapper and return a reference to the old value.
55    ///
56    /// Concurrent readers may continue to see the old Arc pointer until the RCU state machine has
57    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
58    pub fn update_swap<'a>(&self, scope: &'a RcuReadScope, data: Arc<T>) -> Arc<T> {
59        let ptr = Self::into_ptr(data);
60        // SAFETY: We can pass `Self::into_ptr` to `Self::replace_swap`.
61        unsafe { self.replace_swap(scope, ptr) }
62    }
63
64    /// Create a new `Arc` to the object referenced by the wrapped Arc.
65    ///
66    /// This function returns a new `Arc` to the object referenced by the wrapped Arc,
67    /// increasing the reference count of the object by one.
68    pub fn to_arc(&self) -> Arc<T> {
69        let scope = RcuReadScope::new();
70        let ptr = self.ptr.read(&scope);
71        // SAFETY: We can pass `self.ptr` to `rcu_ptr_to_arc` because it was obtained from
72        // `Arc::into_raw`.
73        unsafe { rcu_ptr_to_arc(ptr) }
74    }
75
76    /// Extract the raw pointer from an `Arc`.
77    ///
78    /// The caller is responsible for ensuring that the pointer returned by this function is
79    /// eventually converted back into an `Arc` to balance its reference count.
80    fn into_ptr(data: Arc<T>) -> *mut T {
81        Arc::into_raw(data) as *mut T
82    }
83
84    /// Replace the Arc pointer in the RCU wrapper with a new pointer.
85    ///
86    /// # Safety
87    ///
88    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
89    unsafe fn replace(&self, ptr: *mut T) {
90        let old_ptr = self.ptr.replace(ptr);
91        let arc = unsafe { Arc::from_raw(old_ptr) };
92        rcu_drop(arc);
93    }
94
95    /// Replace the Arc pointer in the RCU wrapper with a new pointer and return a reference to the
96    /// old value.
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_swap<'a>(&self, scope: &'a RcuReadScope, ptr: *mut T) -> Arc<T> {
102        let old_ptr_ref = self.ptr.swap(scope, ptr);
103        // SAFETY: `old_ptr_ref` points to an existing `Arc<T>` with a strong reference.
104        let rcu_arc = unsafe { Arc::from_raw(old_ptr_ref.as_ptr()) };
105        let old_arc = rcu_arc.clone();
106        rcu_drop(rcu_arc);
107        old_arc
108    }
109}
110
111impl<T: RcuDroppable + Sync> Drop for RcuDroppableArc<T> {
112    fn drop(&mut self) {
113        // SAFETY: We can pass `std::ptr::null_mut`.
114        unsafe { self.replace(std::ptr::null_mut()) };
115    }
116}
117
118impl<T: RcuDroppable + Sync> Clone for RcuDroppableArc<T> {
119    fn clone(&self) -> Self {
120        Self::new(self.to_arc())
121    }
122}
123
124impl<T: RcuDroppable + Sync> From<Arc<T>> for RcuDroppableArc<T> {
125    fn from(data: Arc<T>) -> Self {
126        Self::new(data)
127    }
128}
129
130impl<T: Default + RcuDroppable + Sync> Default for RcuDroppableArc<T> {
131    fn default() -> Self {
132        Self::new(Arc::new(T::default()))
133    }
134}
135
136/// Reconstruct an `Arc` from an `RcuPtrRef` by incrementing its strong count.
137///
138/// # Safety
139///
140/// The caller must guarantee that the pointer was obtained from `Arc::into_raw()` and that the
141/// Arc's strong count is non zero.
142///
143/// If the underlying Arc<T> strong count may drop to zero, such as by having outstanding Weak
144/// pointers, use [rcu_ptr_upgrade] to first check that the pointer is valid to reconstruct.
145pub unsafe fn rcu_ptr_to_arc<'a, T>(ptr: RcuPtrRef<'a, T>) -> Arc<T> {
146    let raw_ptr = ptr.as_ptr();
147    unsafe {
148        Arc::increment_strong_count(raw_ptr);
149        Arc::from_raw(raw_ptr)
150    }
151}
152
153/// Reconstruct an `Arc` from an `RcuPtrRef` by upgrading its strong count if it's safe to do so.
154///
155/// Returns `None` if the strong count of the underlying `Arc` has dropped to zero or if the
156/// pointer is null.
157///
158/// # Safety
159///
160/// The caller must guarantee that the pointer was obtained from `Arc::into_raw()` or
161/// `Weak::into_raw()`.
162pub unsafe fn rcu_ptr_upgrade<'a, T>(ptr: RcuPtrRef<'a, T>) -> Option<Arc<T>> {
163    let raw_ptr = ptr.as_ptr();
164    if raw_ptr.is_null() {
165        return None;
166    }
167    // SAFETY: The caller guarantees `raw_ptr` comes from `Arc::into_raw` or `Weak::into_raw`
168    //
169    // The allocation is valid for the duration of the RcuReadScope. We pass the pointer through a
170    // std::sync::Weak to safely increase the Strong count only if it's valid to do so.
171    // ManuallyDrop ensures we don't actually inc/dec the weak count on the Arc.
172    unsafe { std::mem::ManuallyDrop::new(std::sync::Weak::from_raw(raw_ptr)).upgrade() }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::state_machine::rcu_run_callbacks;
179    use std::sync::atomic::{AtomicUsize, Ordering};
180
181    struct DropCounter {
182        value: usize,
183        drops: Arc<AtomicUsize>,
184    }
185
186    // SAFETY: DropCounter only increments an atomic counter on drop.
187    unsafe impl RcuDroppable for DropCounter {}
188
189    impl DropCounter {
190        pub fn new(value: usize) -> Arc<Self> {
191            Arc::new(Self { value, drops: Arc::new(AtomicUsize::new(0)) })
192        }
193    }
194
195    impl Drop for DropCounter {
196        fn drop(&mut self) {
197            self.drops.fetch_add(1, Ordering::Relaxed);
198        }
199    }
200
201    #[test]
202    fn test_rcu_droppable_arc_update() {
203        let object = DropCounter::new(42);
204        let drops = object.drops.clone();
205
206        let arc = RcuDroppableArc::from(object);
207        assert_eq!(arc.read().value, 42);
208        assert_eq!(drops.load(Ordering::Relaxed), 0);
209        arc.update(DropCounter::new(43));
210        assert_eq!(arc.read().value, 43);
211        assert_eq!(drops.load(Ordering::Relaxed), 0);
212
213        rcu_run_callbacks();
214        assert_eq!(drops.load(Ordering::Relaxed), 1);
215    }
216
217    #[test]
218    fn test_rcu_droppable_arc_update_swap() {
219        let object = DropCounter::new(42);
220        let drops = object.drops.clone();
221
222        let arc = RcuDroppableArc::from(object);
223        {
224            let scope = RcuReadScope::new();
225            let old_object = arc.update_swap(&scope, DropCounter::new(43));
226            assert_eq!(old_object.value, 42);
227            assert_eq!(arc.read().value, 43);
228            assert_eq!(drops.load(Ordering::Relaxed), 0);
229        }
230
231        rcu_run_callbacks();
232        assert_eq!(drops.load(Ordering::Relaxed), 1);
233    }
234
235    #[test]
236    fn test_rcu_ptr_upgrade() {
237        let scope = RcuReadScope::new();
238        let null_ptr: RcuPtrRef<'_, DropCounter> = RcuPtrRef::null();
239        assert!(unsafe { rcu_ptr_upgrade(null_ptr) }.is_none());
240
241        let object = DropCounter::new(42);
242        let _weak = Arc::downgrade(&object);
243        let raw = Arc::into_raw(object);
244        let ptr_ref = unsafe { RcuPtrRef::new(&scope, raw) };
245
246        {
247            let upgraded = unsafe { rcu_ptr_upgrade(ptr_ref) };
248            assert!(upgraded.is_some());
249            assert_eq!(upgraded.unwrap().value, 42);
250        }
251
252        // Drop the strong Arc while holding a Weak.
253        let arc = unsafe { Arc::from_raw(raw) };
254        drop(arc);
255
256        // The strong count is 0.
257        assert!(unsafe { rcu_ptr_upgrade(ptr_ref) }.is_none());
258    }
259}