Skip to main content

fuchsia_rcu/
rcu_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
11/// An RCU (Read-Copy-Update) wrapper around an `Arc`.
12///
13/// The Arc can be dereferenced from multiple threads concurrently without blocking.
14/// When the Arc is replaced, reads may continue to see the old Arc pointer for some period of time.
15#[derive(Debug)]
16pub struct RcuArc<T: Send + Sync + 'static> {
17    ptr: RcuPtr<T>,
18}
19
20impl<T: Send + Sync + 'static> RcuArc<T> {
21    /// Create a new RCU wrapper around an `Arc`.
22    pub fn new(data: Arc<T>) -> Self {
23        Self { ptr: RcuPtr::new(Self::into_ptr(data)) }
24    }
25
26    /// Read the value of the wrapped Arc.
27    ///
28    /// The object referenced by the RCU Arc will remain valid until the `RcuReadGuard` is dropped.
29    /// However, another thread running concurrently might see a different value for the object.
30    pub fn read(&self) -> RcuReadGuard<T> {
31        self.ptr.get()
32    }
33
34    /// Returns a reference to the value of the wrapped Arc.
35    ///
36    /// The object referenced by the RCU Arc will remain valid until the `RcuReadScope` is dropped.
37    /// However, another thread running concurrently might see a different value for the object.
38    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> &'a T {
39        self.ptr.read(scope).as_ref().unwrap()
40    }
41
42    /// Write a new Arc to the RCU wrapper.
43    ///
44    /// Concurrent readers may continue to see the old Arc pointer until the RCU state machine has
45    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
46    pub fn update(&self, data: Arc<T>) {
47        let ptr = Self::into_ptr(data);
48        // SAFETY: We can pass `Self::into_ptr` to `Self::replace`.
49        unsafe { self.replace(ptr) };
50    }
51
52    /// Write a new Arc to the RCU wrapper and return a reference to the old value.
53    ///
54    /// Concurrent readers may continue to see the old Arc pointer until the RCU state machine has
55    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
56    pub fn update_swap<'a>(&self, scope: &'a RcuReadScope, data: Arc<T>) -> Arc<T> {
57        let ptr = Self::into_ptr(data);
58        // SAFETY: We can pass `Self::into_ptr` to `Self::replace_swap`.
59        unsafe { self.replace_swap(scope, ptr) }
60    }
61
62    /// Create a new `Arc` to the object referenced by the wrapped Arc.
63    ///
64    /// This function returns a new `Arc` to the object referenced by the wrapped Arc,
65    /// increasing the reference count of the object by one.
66    pub fn to_arc(&self) -> Arc<T> {
67        let scope = RcuReadScope::new();
68        let ptr = self.ptr.read(&scope);
69        // SAFETY: We can pass `self.ptr` to `rcu_ptr_to_arc` because it was obtained from
70        // `Arc::into_raw`.
71        unsafe { rcu_ptr_to_arc(ptr) }
72    }
73
74    /// Extract the raw pointer from an `Arc`.
75    ///
76    /// The caller is responsible for ensuring that the pointer returned by this function is
77    /// eventually converted back into an `Arc` to balance its reference count.
78    fn into_ptr(data: Arc<T>) -> *mut T {
79        Arc::into_raw(data) as *mut T
80    }
81
82    /// Replace the Arc pointer in the RCU wrapper with a new pointer.
83    ///
84    /// # Safety
85    ///
86    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
87    unsafe fn replace(&self, ptr: *mut T) {
88        let old_ptr = self.ptr.replace(ptr);
89        let arc = unsafe { Arc::from_raw(old_ptr) };
90        rcu_drop(arc);
91    }
92
93    /// Replace the Arc pointer in the RCU wrapper with a new pointer and return a reference to the
94    /// old value.
95    ///
96    /// # Safety
97    ///
98    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
99    unsafe fn replace_swap<'a>(&self, scope: &'a RcuReadScope, ptr: *mut T) -> Arc<T> {
100        let old_ptr_ref = self.ptr.swap(scope, ptr);
101        // SAFETY: `old_ptr_ref` points to an existing `Arc<T>` with a strong reference.
102        let rcu_arc = unsafe { Arc::from_raw(old_ptr_ref.as_ptr()) };
103        let old_arc = rcu_arc.clone();
104        rcu_drop(rcu_arc);
105        old_arc
106    }
107}
108
109impl<T: Send + Sync + 'static> Drop for RcuArc<T> {
110    fn drop(&mut self) {
111        // SAFETY: We can pass `std::ptr::null_mut`.
112        unsafe { self.replace(std::ptr::null_mut()) };
113    }
114}
115
116impl<T: Send + Sync + 'static> Clone for RcuArc<T> {
117    fn clone(&self) -> Self {
118        Self::new(self.to_arc())
119    }
120}
121
122impl<T: Send + Sync + 'static> From<Arc<T>> for RcuArc<T> {
123    fn from(data: Arc<T>) -> Self {
124        Self::new(data)
125    }
126}
127
128impl<T: Default + Send + Sync + 'static> Default for RcuArc<T> {
129    fn default() -> Self {
130        Self::new(Arc::new(T::default()))
131    }
132}
133
134/// Reconstruct an `Arc` from an `RcuPtrRef` by incrementing its strong count.
135///
136/// # Safety
137///
138/// The caller must guarantee that the pointer was obtained from `Arc::into_raw()`.
139pub unsafe fn rcu_ptr_to_arc<'a, T>(ptr: RcuPtrRef<'a, T>) -> Arc<T> {
140    let raw_ptr = ptr.as_ptr();
141    unsafe {
142        Arc::increment_strong_count(raw_ptr);
143        Arc::from_raw(raw_ptr)
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use crate::state_machine::rcu_synchronize;
151    use std::sync::atomic::{AtomicUsize, Ordering};
152
153    struct DropCounter {
154        value: usize,
155        drops: Arc<AtomicUsize>,
156    }
157
158    impl DropCounter {
159        pub fn new(value: usize) -> Arc<Self> {
160            Arc::new(Self { value, drops: Arc::new(AtomicUsize::new(0)) })
161        }
162    }
163
164    impl Drop for DropCounter {
165        fn drop(&mut self) {
166            self.drops.fetch_add(1, Ordering::Relaxed);
167        }
168    }
169
170    #[test]
171    fn test_rcu_arc_update() {
172        let object = DropCounter::new(42);
173        let drops = object.drops.clone();
174
175        let arc = RcuArc::from(object);
176        assert_eq!(arc.read().value, 42);
177        assert_eq!(drops.load(Ordering::Relaxed), 0);
178        arc.update(DropCounter::new(43));
179        assert_eq!(arc.read().value, 43);
180        assert_eq!(drops.load(Ordering::Relaxed), 0);
181
182        rcu_synchronize();
183        assert_eq!(drops.load(Ordering::Relaxed), 1);
184    }
185
186    #[test]
187    fn test_rcu_arc_update_swap() {
188        let object = DropCounter::new(42);
189        let drops = object.drops.clone();
190
191        let arc = RcuArc::from(object);
192        {
193            let scope = RcuReadScope::new();
194            let old_object = arc.update_swap(&scope, DropCounter::new(43));
195            assert_eq!(old_object.value, 42);
196            assert_eq!(arc.read().value, 43);
197            assert_eq!(drops.load(Ordering::Relaxed), 0);
198        }
199
200        rcu_synchronize();
201        assert_eq!(drops.load(Ordering::Relaxed), 1);
202    }
203}