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