fuchsia_rcu/rcu_droppable_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 crate::subtle::RcuPtrRef;
9use std::sync::Arc;
10
11use crate::rcu_droppable::RcuDroppable;
12
13/// An RCU (Read-Copy-Update) wrapper around an `Arc` for types implementing [`RcuDroppable`].
14///
15/// The Arc can be dereferenced from multiple threads concurrently without blocking.
16/// When the Arc is replaced, reads may continue to see the old Arc pointer for some period of time.
17#[derive(Debug)]
18pub struct RcuDroppableArc<T: RcuDroppable + Sync> {
19 ptr: RcuPtr<T>,
20}
21
22impl<T: RcuDroppable + Sync> RcuDroppableArc<T> {
23 /// Create a new RCU wrapper around an `Arc`.
24 pub fn new(data: Arc<T>) -> Self {
25 Self { ptr: RcuPtr::new(Self::into_ptr(data)) }
26 }
27
28 /// Read the value of the wrapped Arc.
29 ///
30 /// The object referenced by the RCU Arc will remain valid until the `RcuReadGuard` is dropped.
31 /// However, another thread running concurrently might see a different value for the object.
32 pub fn read(&self) -> RcuReadGuard<T> {
33 self.ptr.get()
34 }
35
36 /// Returns a reference to the value of the wrapped Arc.
37 ///
38 /// The object referenced by the RCU Arc will remain valid until the `RcuReadScope` is dropped.
39 /// However, another thread running concurrently might see a different value for the object.
40 pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> &'a T {
41 self.ptr.read(scope).as_ref().unwrap()
42 }
43
44 /// Write a new Arc to the RCU wrapper.
45 ///
46 /// Concurrent readers may continue to see the old Arc pointer until the RCU state machine has
47 /// made sufficient progress to ensure that no concurrent readers are holding read guards.
48 pub fn update(&self, data: Arc<T>) {
49 let ptr = Self::into_ptr(data);
50 // SAFETY: We can pass `Self::into_ptr` to `Self::replace`.
51 unsafe { self.replace(ptr) };
52 }
53
54 /// Write a new Arc to the RCU wrapper and return a reference to the old value.
55 ///
56 /// Concurrent readers may continue to see the old Arc pointer until the RCU state machine has
57 /// made sufficient progress to ensure that no concurrent readers are holding read guards.
58 pub fn update_swap<'a>(&self, scope: &'a RcuReadScope, data: Arc<T>) -> Arc<T> {
59 let ptr = Self::into_ptr(data);
60 // SAFETY: We can pass `Self::into_ptr` to `Self::replace_swap`.
61 unsafe { self.replace_swap(scope, ptr) }
62 }
63
64 /// Create a new `Arc` to the object referenced by the wrapped Arc.
65 ///
66 /// This function returns a new `Arc` to the object referenced by the wrapped Arc,
67 /// increasing the reference count of the object by one.
68 pub fn to_arc(&self) -> Arc<T> {
69 let scope = RcuReadScope::new();
70 let ptr = self.ptr.read(&scope);
71 // SAFETY: We can pass `self.ptr` to `rcu_ptr_to_arc` because it was obtained from
72 // `Arc::into_raw`.
73 unsafe { rcu_ptr_to_arc(ptr) }
74 }
75
76 /// Extract the raw pointer from an `Arc`.
77 ///
78 /// The caller is responsible for ensuring that the pointer returned by this function is
79 /// eventually converted back into an `Arc` to balance its reference count.
80 fn into_ptr(data: Arc<T>) -> *mut T {
81 Arc::into_raw(data) as *mut T
82 }
83
84 /// Replace the Arc pointer in the RCU wrapper with a new pointer.
85 ///
86 /// # Safety
87 ///
88 /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
89 unsafe fn replace(&self, ptr: *mut T) {
90 let old_ptr = self.ptr.replace(ptr);
91 let arc = unsafe { Arc::from_raw(old_ptr) };
92 rcu_drop(arc);
93 }
94
95 /// Replace the Arc pointer in the RCU wrapper with a new pointer and return a reference to the
96 /// old value.
97 ///
98 /// # Safety
99 ///
100 /// The caller must have obtained the pointer from `Self::into_ptr` or from `std::ptr::null_mut`.
101 unsafe fn replace_swap<'a>(&self, scope: &'a RcuReadScope, ptr: *mut T) -> Arc<T> {
102 let old_ptr_ref = self.ptr.swap(scope, ptr);
103 // SAFETY: `old_ptr_ref` points to an existing `Arc<T>` with a strong reference.
104 let rcu_arc = unsafe { Arc::from_raw(old_ptr_ref.as_ptr()) };
105 let old_arc = rcu_arc.clone();
106 rcu_drop(rcu_arc);
107 old_arc
108 }
109}
110
111impl<T: RcuDroppable + Sync> Drop for RcuDroppableArc<T> {
112 fn drop(&mut self) {
113 // SAFETY: We can pass `std::ptr::null_mut`.
114 unsafe { self.replace(std::ptr::null_mut()) };
115 }
116}
117
118impl<T: RcuDroppable + Sync> Clone for RcuDroppableArc<T> {
119 fn clone(&self) -> Self {
120 Self::new(self.to_arc())
121 }
122}
123
124impl<T: RcuDroppable + Sync> From<Arc<T>> for RcuDroppableArc<T> {
125 fn from(data: Arc<T>) -> Self {
126 Self::new(data)
127 }
128}
129
130impl<T: Default + RcuDroppable + Sync> Default for RcuDroppableArc<T> {
131 fn default() -> Self {
132 Self::new(Arc::new(T::default()))
133 }
134}
135
136/// Reconstruct an `Arc` from an `RcuPtrRef` by incrementing its strong count.
137///
138/// # Safety
139///
140/// The caller must guarantee that the pointer was obtained from `Arc::into_raw()` and that the
141/// Arc's strong count is non zero.
142///
143/// If the underlying Arc<T> strong count may drop to zero, such as by having outstanding Weak
144/// pointers, use [rcu_ptr_upgrade] to first check that the pointer is valid to reconstruct.
145pub unsafe fn rcu_ptr_to_arc<'a, T>(ptr: RcuPtrRef<'a, T>) -> Arc<T> {
146 let raw_ptr = ptr.as_ptr();
147 unsafe {
148 Arc::increment_strong_count(raw_ptr);
149 Arc::from_raw(raw_ptr)
150 }
151}
152
153/// Reconstruct an `Arc` from an `RcuPtrRef` by upgrading its strong count if it's safe to do so.
154///
155/// Returns `None` if the strong count of the underlying `Arc` has dropped to zero or if the
156/// pointer is null.
157///
158/// # Safety
159///
160/// The caller must guarantee that the pointer was obtained from `Arc::into_raw()` or
161/// `Weak::into_raw()`.
162pub unsafe fn rcu_ptr_upgrade<'a, T>(ptr: RcuPtrRef<'a, T>) -> Option<Arc<T>> {
163 let raw_ptr = ptr.as_ptr();
164 if raw_ptr.is_null() {
165 return None;
166 }
167 // SAFETY: The caller guarantees `raw_ptr` comes from `Arc::into_raw` or `Weak::into_raw`
168 //
169 // The allocation is valid for the duration of the RcuReadScope. We pass the pointer through a
170 // std::sync::Weak to safely increase the Strong count only if it's valid to do so.
171 // ManuallyDrop ensures we don't actually inc/dec the weak count on the Arc.
172 unsafe { std::mem::ManuallyDrop::new(std::sync::Weak::from_raw(raw_ptr)).upgrade() }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::state_machine::rcu_run_callbacks;
179 use std::sync::atomic::{AtomicUsize, Ordering};
180
181 struct DropCounter {
182 value: usize,
183 drops: Arc<AtomicUsize>,
184 }
185
186 // SAFETY: DropCounter only increments an atomic counter on drop.
187 unsafe impl RcuDroppable for DropCounter {}
188
189 impl DropCounter {
190 pub fn new(value: usize) -> Arc<Self> {
191 Arc::new(Self { value, drops: Arc::new(AtomicUsize::new(0)) })
192 }
193 }
194
195 impl Drop for DropCounter {
196 fn drop(&mut self) {
197 self.drops.fetch_add(1, Ordering::Relaxed);
198 }
199 }
200
201 #[test]
202 fn test_rcu_droppable_arc_update() {
203 let object = DropCounter::new(42);
204 let drops = object.drops.clone();
205
206 let arc = RcuDroppableArc::from(object);
207 assert_eq!(arc.read().value, 42);
208 assert_eq!(drops.load(Ordering::Relaxed), 0);
209 arc.update(DropCounter::new(43));
210 assert_eq!(arc.read().value, 43);
211 assert_eq!(drops.load(Ordering::Relaxed), 0);
212
213 rcu_run_callbacks();
214 assert_eq!(drops.load(Ordering::Relaxed), 1);
215 }
216
217 #[test]
218 fn test_rcu_droppable_arc_update_swap() {
219 let object = DropCounter::new(42);
220 let drops = object.drops.clone();
221
222 let arc = RcuDroppableArc::from(object);
223 {
224 let scope = RcuReadScope::new();
225 let old_object = arc.update_swap(&scope, DropCounter::new(43));
226 assert_eq!(old_object.value, 42);
227 assert_eq!(arc.read().value, 43);
228 assert_eq!(drops.load(Ordering::Relaxed), 0);
229 }
230
231 rcu_run_callbacks();
232 assert_eq!(drops.load(Ordering::Relaxed), 1);
233 }
234
235 #[test]
236 fn test_rcu_ptr_upgrade() {
237 let scope = RcuReadScope::new();
238 let null_ptr: RcuPtrRef<'_, DropCounter> = RcuPtrRef::null();
239 assert!(unsafe { rcu_ptr_upgrade(null_ptr) }.is_none());
240
241 let object = DropCounter::new(42);
242 let _weak = Arc::downgrade(&object);
243 let raw = Arc::into_raw(object);
244 let ptr_ref = unsafe { RcuPtrRef::new(&scope, raw) };
245
246 {
247 let upgraded = unsafe { rcu_ptr_upgrade(ptr_ref) };
248 assert!(upgraded.is_some());
249 assert_eq!(upgraded.unwrap().value, 42);
250 }
251
252 // Drop the strong Arc while holding a Weak.
253 let arc = unsafe { Arc::from_raw(raw) };
254 drop(arc);
255
256 // The strong count is 0.
257 assert!(unsafe { rcu_ptr_upgrade(ptr_ref) }.is_none());
258 }
259}