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