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) version of `Option<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 RcuOptionArc<T: Send + Sync + 'static> {
17    ptr: RcuPtr<T>,
18}
19
20impl<T: Send + Sync + 'static> RcuOptionArc<T> {
21    /// Create a new `RcuOptionArc` from an `Option<Arc<T>>`.
22    pub fn new(data: Option<Arc<T>>) -> Self {
23        Self { ptr: RcuPtr::new(Self::into_ptr(data)) }
24    }
25
26    /// Read the value of the `RcuOptionArc`.
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) -> Option<RcuReadGuard<T>> {
31        self.ptr.maybe_get()
32    }
33
34    /// Returns a reference to the value of the `RcuOptionArc`.
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) -> Option<&'a T> {
39        self.ptr.read(scope).as_ref()
40    }
41
42    /// Write the value of the `RcuOptionArc`.
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: Option<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 `Option<Arc<T>>` to the object referenced by the `RcuOptionArc`.
53    ///
54    /// This function returns a new `Option<Arc<T>>` to the object referenced by the `RcuOptionArc`,
55    /// potentially increasing the reference count of the object by one.
56    pub fn to_option_arc(&self) -> Option<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            Some(Arc::from_raw(ptr))
64        }
65    }
66
67    /// Extract the raw pointer from an `Option<Arc<T>>`.
68    ///
69    /// The caller is responsible for ensuring that the pointer returned by this function is
70    /// eventually converted back into an `Option<Arc<T>>` to balance its reference count.
71    fn into_ptr(data: Option<Arc<T>>) -> *mut T {
72        match data {
73            Some(arc) => Arc::into_raw(arc) as *mut T,
74            None => std::ptr::null_mut(),
75        }
76    }
77
78    /// Replace the pointer in the `RcuOptionArc` with a new pointer.
79    ///
80    /// # Safety
81    ///
82    /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
83    unsafe fn replace(&self, ptr: *mut T) {
84        let old_ptr = self.ptr.replace(ptr);
85        if !old_ptr.is_null() {
86            let arc = unsafe { Arc::from_raw(old_ptr) };
87            rcu_drop(arc);
88        }
89    }
90}
91
92impl<T: Send + Sync + 'static> Drop for RcuOptionArc<T> {
93    fn drop(&mut self) {
94        // SAFETY: We can pass `std::ptr::null_mut`.
95        unsafe { self.replace(std::ptr::null_mut()) };
96    }
97}
98
99impl<T: Send + Sync + 'static> Clone for RcuOptionArc<T> {
100    fn clone(&self) -> Self {
101        Self::new(self.to_option_arc())
102    }
103}
104
105impl<T: Send + Sync + 'static> From<Option<Arc<T>>> for RcuOptionArc<T> {
106    fn from(data: Option<Arc<T>>) -> Self {
107        Self::new(data)
108    }
109}
110
111impl<T: Send + Sync + 'static> Default for RcuOptionArc<T> {
112    fn default() -> Self {
113        Self::new(None)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    use crate::state_machine::rcu_synchronize;
121    use std::sync::atomic::{AtomicUsize, Ordering};
122
123    struct DropCounter {
124        value: usize,
125        drops: Arc<AtomicUsize>,
126    }
127
128    impl DropCounter {
129        pub fn new(value: usize) -> Arc<Self> {
130            Arc::new(Self { value, drops: Arc::new(AtomicUsize::new(0)) })
131        }
132    }
133
134    impl Drop for DropCounter {
135        fn drop(&mut self) {
136            self.drops.fetch_add(1, Ordering::Relaxed);
137        }
138    }
139
140    #[test]
141    fn test_rcu_option_arc() {
142        let object = DropCounter::new(42);
143        let drops = object.drops.clone();
144
145        let arc = RcuOptionArc::from(Some(object));
146        assert_eq!(arc.read().unwrap().value, 42);
147        assert_eq!(drops.load(Ordering::Relaxed), 0);
148        arc.update(Some(DropCounter::new(43)));
149        assert_eq!(arc.read().unwrap().value, 43);
150        assert_eq!(drops.load(Ordering::Relaxed), 0);
151
152        rcu_synchronize();
153        assert_eq!(drops.load(Ordering::Relaxed), 1);
154    }
155
156    #[test]
157    fn test_rcu_option_arc_none() {
158        let object = DropCounter::new(42);
159        let drops = object.drops.clone();
160
161        let arc = RcuOptionArc::from(Some(object));
162        assert_eq!(arc.read().unwrap().value, 42);
163        assert_eq!(drops.load(Ordering::Relaxed), 0);
164
165        arc.update(None);
166        assert!(arc.read().is_none());
167        assert_eq!(drops.load(Ordering::Relaxed), 0);
168
169        rcu_synchronize();
170        assert_eq!(drops.load(Ordering::Relaxed), 1);
171    }
172
173    #[test]
174    fn test_rcu_option_arc_default() {
175        let arc: RcuOptionArc<DropCounter> = RcuOptionArc::default();
176        assert!(arc.read().is_none());
177    }
178
179    #[test]
180    fn test_rcu_option_arc_to_option_arc_none() {
181        let arc: RcuOptionArc<DropCounter> = RcuOptionArc::default();
182        assert!(arc.to_option_arc().is_none());
183    }
184}