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::state_machine::rcu_drop;
7use std::sync::Arc;
8
9/// An RCU (Read-Copy-Update) version of `Arc`.
10///
11/// This Arc can be read from multiple threads concurrently without blocking.
12/// When the Arc is written, reads may continue to see the old value of the Arc
13/// for some period of time.
14#[derive(Debug)]
15pub struct RcuArc<T: Send + Sync + 'static> {
16    ptr: RcuPtr<T>,
17}
18
19impl<T: Send + Sync + 'static> RcuArc<T> {
20    /// Create a new RCU Arc from an `Arc`.
21    pub fn new(data: Arc<T>) -> Self {
22        Self { ptr: RcuPtr::new(Self::into_ptr(data)) }
23    }
24
25    /// Read the value of the RCU Arc.
26    ///
27    /// The object referenced by the RCU Arc will remain valid until the `RcuReadGuard` is dropped.
28    /// However, another thread running concurrently might see a different value for the object.
29    pub fn read(&self) -> RcuReadGuard<T> {
30        self.ptr.get()
31    }
32
33    /// Write the value of the RCU Arc.
34    ///
35    /// Concurrent readers may continue to see the old value of the Arc until the RCU state machine
36    /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
37    pub fn update(&self, data: Arc<T>) {
38        let ptr = Self::into_ptr(data);
39        // SAFETY: We can pass `Self::into_ptr` to `Self::replace`.
40        unsafe { self.replace(ptr) };
41    }
42
43    /// Create a new `Arc` to the object referenced by the RCU Arc.
44    ///
45    /// This function returns a new `Arc` to the object referenced by the RCU Arc, increasing the
46    /// reference count of the object by one.
47    pub fn to_arc(&self) -> Arc<T> {
48        let guard = self.read();
49        let ptr = guard.as_ptr();
50        // SAFETY: We can make a new Arc to the object by incrementing the strong count and then
51        // converting the pointer to an Arc.
52        unsafe {
53            Arc::increment_strong_count(ptr);
54            Arc::from_raw(ptr)
55        }
56    }
57
58    /// Extract the raw pointer from an `Arc`.
59    ///
60    /// The caller is responsible for ensuring that the pointer returned by this function is
61    /// eventually converted back into an `Arc` to balance its reference count.
62    fn into_ptr(data: Arc<T>) -> *mut T {
63        Arc::into_raw(data) as *mut T
64    }
65
66    /// Replace the pointer in the RCU Arc with a new pointer.
67    ///
68    /// # Safety
69    ///
70    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
71    unsafe fn replace(&self, ptr: *mut T) {
72        let old_ptr = self.ptr.replace(ptr);
73        let arc = unsafe { Arc::from_raw(old_ptr) };
74        rcu_drop(arc);
75    }
76}
77
78impl<T: Send + Sync + 'static> Drop for RcuArc<T> {
79    fn drop(&mut self) {
80        // SAFETY: We can pass `std::ptr::null_mut`.
81        unsafe { self.replace(std::ptr::null_mut()) };
82    }
83}
84
85impl<T: Send + Sync + 'static> Clone for RcuArc<T> {
86    fn clone(&self) -> Self {
87        Self::new(self.to_arc())
88    }
89}
90
91impl<T: Send + Sync + 'static> From<Arc<T>> for RcuArc<T> {
92    fn from(data: Arc<T>) -> Self {
93        Self::new(data)
94    }
95}
96
97impl<T: Default + Send + Sync + 'static> Default for RcuArc<T> {
98    fn default() -> Self {
99        Self::new(Arc::new(T::default()))
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use crate::state_machine::rcu_synchronize;
107    use std::sync::atomic::{AtomicUsize, Ordering};
108
109    struct DropCounter {
110        value: usize,
111        drops: Arc<AtomicUsize>,
112    }
113
114    impl DropCounter {
115        pub fn new(value: usize) -> Arc<Self> {
116            Arc::new(Self { value, drops: Arc::new(AtomicUsize::new(0)) })
117        }
118    }
119
120    impl Drop for DropCounter {
121        fn drop(&mut self) {
122            self.drops.fetch_add(1, Ordering::Relaxed);
123        }
124    }
125
126    #[test]
127    fn test_rcu_arc_update() {
128        let object = DropCounter::new(42);
129        let drops = object.drops.clone();
130
131        let arc = RcuArc::from(object);
132        assert_eq!(arc.read().value, 42);
133        assert_eq!(drops.load(Ordering::Relaxed), 0);
134        arc.update(DropCounter::new(43));
135        assert_eq!(arc.read().value, 43);
136        assert_eq!(drops.load(Ordering::Relaxed), 0);
137
138        rcu_synchronize();
139        assert_eq!(drops.load(Ordering::Relaxed), 1);
140    }
141}