Skip to main content

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    /// Returns `true` if the RCU pointer is currently null.
80    ///
81    /// This does not require an `RcuReadScope` because the pointer is never dereferenced. Another
82    /// thread running concurrently might see a different value.
83    pub fn is_null(&self) -> bool {
84        rcu_read_pointer(&self.ptr).is_null()
85    }
86
87    /// Assign a new value to the RCU pointer.
88    ///
89    /// Concurrent readers may continue to see the old value of the pointer until the RCU state
90    /// machine has made sufficient progress. To wait until all concurrent readers have dropped
91    /// their read guards, call `rcu_synchronize()`.
92    pub fn assign(&self, ptr: *mut T) {
93        rcu_assign_pointer(&self.ptr, ptr);
94    }
95
96    /// Assign a new value to the RCU pointer.
97    ///
98    /// Concurrent readers may continue to see the old value of the pointer until the RCU state
99    /// machine has made sufficient progress. To wait until all concurrent readers have dropped
100    /// their read guards, call `rcu_synchronize()`.
101    pub fn assign_ptr(&self, ptr: RcuPtrRef<'_, T>) {
102        self.assign(ptr.as_mut_ptr());
103    }
104
105    /// Replace the value of the RCU pointer.
106    ///
107    /// Concurrent readers may continue to see the old value of the pointer until the RCU state
108    /// machine has made sufficient progress. To wait until all concurrent readers have dropped
109    /// their read guards, call `rcu_synchronize()`.
110    pub fn replace(&self, ptr: *mut T) -> *mut T {
111        rcu_replace_pointer(&self.ptr, ptr)
112    }
113
114    /// Replace the value of the RCU pointer.
115    ///
116    /// Concurrent readers may continue to see the old value of the pointer until the RCU state
117    /// machine has made sufficient progress. To wait until all concurrent readers have dropped
118    /// their read guards, call `rcu_synchronize()`.
119    pub fn replace_ptr(&self, ptr: RcuPtrRef<'_, T>) -> *mut T {
120        self.replace(ptr.as_mut_ptr())
121    }
122
123    /// Swap the value of the RCU pointer with another pointer.
124    ///
125    /// Concurrent readers may continue to see the old value of the pointer until the RCU state
126    /// machine has made sufficient progress. To wait until all concurrent readers have dropped
127    /// their read guards, call `rcu_synchronize()`.
128    pub fn swap<'a>(&self, scope: &'a RcuReadScope, ptr: *mut T) -> RcuPtrRef<'a, T> {
129        let old_ptr = self.replace(ptr);
130        // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we drop
131        // the RcuReadScope whose lifetime is described by the lifetime parameter.
132        unsafe { RcuPtrRef::new(scope, old_ptr) }
133    }
134
135    /// Poison the RCU pointer.
136    ///
137    /// Poisoning the RCU pointer will cause readers to see a dangling pointer. Useful when the
138    /// pointer is no longer valid for reading.
139    pub fn poison(&self) {
140        rcu_assign_pointer(&self.ptr, std::ptr::dangling_mut());
141    }
142}
143
144/// A read guard for an object managed by the RCU state machine.
145///
146/// This guard ensures that the object remains valid until the guard is dropped.
147pub struct RcuReadGuard<T> {
148    /// The scope in which the object is valid.
149    scope: RcuReadScope,
150
151    /// The pointer to the object.
152    ptr: *const T,
153}
154
155impl<T> RcuReadGuard<T> {
156    /// Get the scope in which the object is valid.
157    pub fn scope(&self) -> &RcuReadScope {
158        &self.scope
159    }
160
161    /// Get the raw pointer to the object.
162    ///
163    /// This function returns the raw pointer to the object. The caller is responsible for ensuring
164    /// that the pointer is not referenced after the guard is dropped.
165    ///
166    /// To use the Rust borrow checker to enforce that the object is not accessed after the guard is
167    /// dropped, use the `Deref` implementation.
168    pub fn as_ptr(&self) -> *const T {
169        self.ptr
170    }
171}
172
173impl<T> Deref for RcuReadGuard<T> {
174    type Target = T;
175    fn deref(&self) -> &Self::Target {
176        // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we drop
177        // the RcuReadScope.
178        unsafe { &*self.ptr }
179    }
180}
181
182/// A pointer to an object managed by the RCU state machine.
183///
184/// This pointer is valid for reading until the `RcuReadScope` is dropped.
185pub struct RcuPtrRef<'a, T> {
186    /// The pointer to the object.
187    ptr: *const T,
188
189    /// The scope in which the pointer is valid.
190    _marker: std::marker::PhantomData<&'a T>,
191}
192
193impl<'a, T> Clone for RcuPtrRef<'a, T> {
194    fn clone(&self) -> Self {
195        Self { ptr: self.ptr, _marker: self._marker }
196    }
197}
198
199impl<'a, T> Copy for RcuPtrRef<'a, T> {}
200
201impl<'a, T> RcuPtrRef<'a, T> {
202    /// Create a new `RcuPtrRef` from a pointer and a scope.
203    ///
204    /// # Safety
205    ///
206    /// The pointer must be valid for reading until the `RcuReadScope` is dropped.
207    pub unsafe fn new(_scope: &'a RcuReadScope, ptr: *const T) -> Self {
208        Self { ptr, _marker: std::marker::PhantomData }
209    }
210
211    /// Create a null `RcuPtrRef`.
212    pub fn null() -> Self {
213        Self { ptr: std::ptr::null(), _marker: std::marker::PhantomData }
214    }
215
216    /// Check if the pointer is null.
217    pub fn is_null(&self) -> bool {
218        self.ptr.is_null()
219    }
220
221    /// Create a new `RcuPtrRef` from a reference.
222    pub fn from_ref(object: &'a T) -> Self {
223        Self { ptr: object as *const T, _marker: std::marker::PhantomData }
224    }
225
226    /// Get a reference to the object.
227    ///
228    /// Returns `None` if the pointer is null.
229    pub fn as_ref(&self) -> Option<&'a T> {
230        if self.is_null() {
231            None
232        } else {
233            // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we
234            // drop the RcuReadScope whose lifetime is described by the lifetime parameter.
235            Some(unsafe { &*self.ptr })
236        }
237    }
238
239    /// Get the raw pointer to the object.
240    pub fn as_ptr(&self) -> *const T {
241        self.ptr
242    }
243
244    /// Get the raw mutable pointer to the object.
245    pub fn as_mut_ptr(&self) -> *mut T {
246        self.ptr as *mut T
247    }
248
249    /// Adds a byte offset to the pointer.
250    ///
251    /// # Safety
252    ///
253    /// The caller must ensure that the offset is within the bounds of the object and points to a
254    /// valid object of type `U`.
255    pub unsafe fn add_byte_offset<U>(&self, offset: usize) -> RcuPtrRef<'a, U> {
256        let ptr = self.ptr as *const u8;
257        // SAFETY: The caller must ensure that the offset is within the bounds of the object and
258        // points to a valid object of type `U`.
259        RcuPtrRef { ptr: unsafe { ptr.add(offset) } as *const U, _marker: std::marker::PhantomData }
260    }
261
262    /// Subtracts a byte offset from the pointer.
263    ///
264    /// # Safety
265    ///
266    /// The caller must ensure that the offset is within the bounds of the object and points to a
267    /// valid object of type `U`.
268    pub unsafe fn sub_byte_offset<U>(&self, offset: usize) -> RcuPtrRef<'a, U> {
269        let ptr = self.ptr as *const u8;
270        // SAFETY: The caller must ensure that the offset is within the bounds of the object and
271        // points to a valid object of type `U`.
272        RcuPtrRef { ptr: unsafe { ptr.sub(offset) } as *const U, _marker: std::marker::PhantomData }
273    }
274}