Skip to main content

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