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