fuchsia_rcu/rcu_ptr.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_read_scope::RcuReadScope;
6use crate::state_machine::{rcu_assign_pointer, rcu_read_pointer, rcu_replace_pointer};
7use std::ops::Deref;
8use std::sync::atomic::AtomicPtr;
9
10/// A pointer managed by the RCU state machine.
11///
12/// This pointer can be read from multiple threads concurrently without blocking.
13/// When the pointer is written, reads may continue to see the old value of the pointer
14/// for some period of time.
15///
16/// Callers are responsible managing the lifetime of the object referenced by the pointer. When the
17/// the pointer value is written, the caller should typically use `rcu_call` or `rcu_drop` to defer
18/// cleanup of the object referenced by the old pointer value until the RCU state machine has made
19/// sufficient progress to ensure that no concurrent readers are holding read guards.
20#[derive(Debug)]
21pub struct RcuPtr<T> {
22 ptr: AtomicPtr<T>,
23}
24
25impl<T> RcuPtr<T> {
26 /// Create a new RCU pointer from a raw pointer.
27 pub fn new(ptr: *mut T) -> Self {
28 Self { ptr: AtomicPtr::new(ptr) }
29 }
30
31 /// Create a new RCU pointer from a reference.
32 pub fn from_ref(reference: &T) -> Self {
33 Self::new(reference as *const T as *mut T)
34 }
35
36 /// Create a null RCU pointer.
37 pub fn null() -> Self {
38 Self { ptr: AtomicPtr::new(std::ptr::null_mut()) }
39 }
40
41 /// Get the value pointed to by the RCU pointer.
42 ///
43 /// If the RCU pointer is null, this function will panic.
44 ///
45 /// The object referenced by the RCU pointer will remain valid until the `RcuReadGuard` is
46 /// dropped. However, another thread running concurrently might see a different value for the
47 /// object.
48 pub fn get(&self) -> RcuReadGuard<T> {
49 let scope = RcuReadScope::new();
50 let ptr = self.read(&scope).as_ptr();
51 assert!(!ptr.is_null());
52 RcuReadGuard { scope, ptr }
53 }
54
55 /// Get the value pointed to by the RCU pointer.
56 ///
57 /// If the RCU pointer is null, this function will return `None`.
58 ///
59 /// The object referenced by the RCU pointer will remain valid until the `RcuReadGuard` is
60 /// dropped. However, another thread running concurrently might see a different value for the
61 /// object.
62 pub fn maybe_get(&self) -> Option<RcuReadGuard<T>> {
63 let scope = RcuReadScope::new();
64 let ptr = self.read(&scope).as_ptr();
65 if ptr.is_null() { None } else { Some(RcuReadGuard { scope, ptr }) }
66 }
67
68 /// Read the value of the RCU pointer.
69 ///
70 /// The returned pointer will remain valid until the `RcuReadScope` is dropped. However, another
71 /// thread running concurrently might see a different value for the object.
72 pub fn read<'a>(&self, scope: &'a RcuReadScope) -> RcuPtrRef<'a, T> {
73 let ptr = rcu_read_pointer(&self.ptr);
74 // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we drop
75 // the RcuReadScope whose lifetime is described by the lifetime parameter.
76 unsafe { RcuPtrRef::new(scope, ptr) }
77 }
78
79 /// Assign a new value to the RCU pointer.
80 ///
81 /// Concurrent readers may continue to see the old value of the pointer until the RCU state
82 /// machine has made sufficient progress. To wait until all concurrent readers have dropped
83 /// their read guards, call `rcu_synchronize()`.
84 pub fn assign(&self, ptr: *mut T) {
85 rcu_assign_pointer(&self.ptr, ptr);
86 }
87
88 /// Assign a new value to the RCU pointer.
89 ///
90 /// Concurrent readers may continue to see the old value of the pointer until the RCU state
91 /// machine has made sufficient progress. To wait until all concurrent readers have dropped
92 /// their read guards, call `rcu_synchronize()`.
93 pub fn assign_ptr(&self, ptr: RcuPtrRef<'_, T>) {
94 self.assign(ptr.as_mut_ptr());
95 }
96
97 /// Replace the value of the RCU pointer.
98 ///
99 /// Concurrent readers may continue to see the old value of the pointer until the RCU state
100 /// machine has made sufficient progress. To wait until all concurrent readers have dropped
101 /// their read guards, call `rcu_synchronize()`.
102 pub fn replace(&self, ptr: *mut T) -> *mut T {
103 rcu_replace_pointer(&self.ptr, ptr)
104 }
105
106 /// Replace the value of the RCU pointer.
107 ///
108 /// Concurrent readers may continue to see the old value of the pointer until the RCU state
109 /// machine has made sufficient progress. To wait until all concurrent readers have dropped
110 /// their read guards, call `rcu_synchronize()`.
111 pub fn replace_ptr(&self, ptr: RcuPtrRef<'_, T>) -> *mut T {
112 self.replace(ptr.as_mut_ptr())
113 }
114
115 /// Swap the value of the RCU pointer with another pointer.
116 ///
117 /// Concurrent readers may continue to see the old value of the pointer until the RCU state
118 /// machine has made sufficient progress. To wait until all concurrent readers have dropped
119 /// their read guards, call `rcu_synchronize()`.
120 pub fn swap<'a>(&self, scope: &'a RcuReadScope, ptr: *mut T) -> RcuPtrRef<'a, T> {
121 let old_ptr = self.replace(ptr);
122 // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we drop
123 // the RcuReadScope whose lifetime is described by the lifetime parameter.
124 unsafe { RcuPtrRef::new(scope, old_ptr) }
125 }
126
127 /// Poison the RCU pointer.
128 ///
129 /// Poisoning the RCU pointer will cause readers to see a dangling pointer. Useful when the
130 /// pointer is no longer valid for reading.
131 pub fn poison(&self) {
132 rcu_assign_pointer(&self.ptr, std::ptr::dangling_mut());
133 }
134}
135
136/// A read guard for an object managed by the RCU state machine.
137///
138/// This guard ensures that the object remains valid until the guard is dropped.
139pub struct RcuReadGuard<T> {
140 /// The scope in which the object is valid.
141 scope: RcuReadScope,
142
143 /// The pointer to the object.
144 ptr: *const T,
145}
146
147impl<T> RcuReadGuard<T> {
148 /// Get the scope in which the object is valid.
149 pub fn scope(&self) -> &RcuReadScope {
150 &self.scope
151 }
152
153 /// Get the raw pointer to the object.
154 ///
155 /// This function returns the raw pointer to the object. The caller is responsible for ensuring
156 /// that the pointer is not referenced after the guard is dropped.
157 ///
158 /// To use the Rust borrow checker to enforce that the object is not accessed after the guard is
159 /// dropped, use the `Deref` implementation.
160 pub fn as_ptr(&self) -> *const T {
161 self.ptr
162 }
163}
164
165impl<T> Deref for RcuReadGuard<T> {
166 type Target = T;
167 fn deref(&self) -> &Self::Target {
168 // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we drop
169 // the RcuReadScope.
170 unsafe { &*self.ptr }
171 }
172}
173
174/// A pointer to an object managed by the RCU state machine.
175///
176/// This pointer is valid for reading until the `RcuReadScope` is dropped.
177pub struct RcuPtrRef<'a, T> {
178 /// The pointer to the object.
179 ptr: *const T,
180
181 /// The scope in which the pointer is valid.
182 _marker: std::marker::PhantomData<&'a T>,
183}
184
185impl<'a, T> Clone for RcuPtrRef<'a, T> {
186 fn clone(&self) -> Self {
187 Self { ptr: self.ptr, _marker: self._marker }
188 }
189}
190
191impl<'a, T> Copy for RcuPtrRef<'a, T> {}
192
193impl<'a, T> RcuPtrRef<'a, T> {
194 /// Create a new `RcuPtrRef` from a pointer and a scope.
195 ///
196 /// # Safety
197 ///
198 /// The pointer must be valid for reading until the `RcuReadScope` is dropped.
199 pub unsafe fn new(_scope: &'a RcuReadScope, ptr: *const T) -> Self {
200 Self { ptr, _marker: std::marker::PhantomData }
201 }
202
203 /// Create a null `RcuPtrRef`.
204 pub fn null() -> Self {
205 Self { ptr: std::ptr::null(), _marker: std::marker::PhantomData }
206 }
207
208 /// Check if the pointer is null.
209 pub fn is_null(&self) -> bool {
210 self.ptr.is_null()
211 }
212
213 /// Create a new `RcuPtrRef` from a reference.
214 pub fn from_ref(object: &'a T) -> Self {
215 Self { ptr: object as *const T, _marker: std::marker::PhantomData }
216 }
217
218 /// Get a reference to the object.
219 ///
220 /// Returns `None` if the pointer is null.
221 pub fn as_ref(&self) -> Option<&'a T> {
222 if self.is_null() {
223 None
224 } else {
225 // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we
226 // drop the RcuReadScope whose lifetime is described by the lifetime parameter.
227 Some(unsafe { &*self.ptr })
228 }
229 }
230
231 /// Get the raw pointer to the object.
232 pub fn as_ptr(&self) -> *const T {
233 self.ptr
234 }
235
236 /// Get the raw mutable pointer to the object.
237 pub fn as_mut_ptr(&self) -> *mut T {
238 self.ptr as *mut T
239 }
240
241 /// Adds a byte offset to the pointer.
242 ///
243 /// # Safety
244 ///
245 /// The caller must ensure that the offset is within the bounds of the object and points to a
246 /// valid object of type `U`.
247 pub unsafe fn add_byte_offset<U>(&self, offset: usize) -> RcuPtrRef<'a, U> {
248 let ptr = self.ptr as *const u8;
249 // SAFETY: The caller must ensure that the offset is within the bounds of the object and
250 // points to a valid object of type `U`.
251 RcuPtrRef { ptr: unsafe { ptr.add(offset) } as *const U, _marker: std::marker::PhantomData }
252 }
253
254 /// Subtracts a byte offset from the pointer.
255 ///
256 /// # Safety
257 ///
258 /// The caller must ensure that the offset is within the bounds of the object and points to a
259 /// valid object of type `U`.
260 pub unsafe fn sub_byte_offset<U>(&self, offset: usize) -> RcuPtrRef<'a, U> {
261 let ptr = self.ptr as *const u8;
262 // SAFETY: The caller must ensure that the offset is within the bounds of the object and
263 // points to a valid object of type `U`.
264 RcuPtrRef { ptr: unsafe { ptr.sub(offset) } as *const U, _marker: std::marker::PhantomData }
265 }
266}