Skip to main content

fuchsia_rcu/
rcu_box.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;
8
9/// An RCU (Read-Copy-Update) wrapper around a `Box`.
10///
11/// The Box can be dereferenced from multiple threads concurrently without blocking.
12/// When the Box is replaced, reads may continue to see the old Box pointer for some period of time.
13#[derive(Debug)]
14pub struct RcuBox<T: Send + Sync + 'static> {
15    ptr: RcuPtr<T>,
16}
17
18impl<T: Send + Sync + 'static> RcuBox<T> {
19    /// Create a new RCU wrapped Box from a value.
20    pub fn new(data: T) -> Self {
21        Self::from(Box::new(data))
22    }
23
24    /// Read the value of the wrapped Box.
25    ///
26    /// The object referenced by the Box will remain valid until the `RcuReadGuard` is dropped.
27    /// However, another thread running concurrently might see a different object.
28    pub fn read(&self) -> RcuReadGuard<T> {
29        self.ptr.get()
30    }
31
32    /// Returns a reference to the value of the wrapped Box.
33    ///
34    /// The object referenced by the Box will remain valid until the `RcuReadScope` is dropped.
35    /// However, another thread running concurrently might see a different object.
36    pub fn as_ref<'a>(&self, scope: &'a RcuReadScope) -> &'a T {
37        self.ptr.read(scope).as_ref().unwrap()
38    }
39
40    /// Write a new Boxed value to the RCU wrapper.
41    ///
42    /// Concurrent readers may continue to see the old boxed object until the RCU state machine has
43    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
44    pub fn update(&self, data: T) {
45        let ptr = Box::into_raw(Box::new(data));
46        // SAFETY: We can pass `Box::into_raw` to `Self::replace`.
47        unsafe { self.replace(ptr) };
48    }
49
50    /// Replace the Box pointer in the RCU wrapper with a new pointer.
51    ///
52    /// # Safety
53    ///
54    /// The pointer must have been created by `Box::into_raw` or from `std::ptr::null_mut`.
55    unsafe fn replace(&self, ptr: *mut T) {
56        let old_ptr = self.ptr.replace(ptr);
57        let object = unsafe { Box::from_raw(old_ptr) };
58        rcu_drop(object);
59    }
60}
61
62impl<T: Clone + Send + Sync + 'static> RcuBox<T> {
63    /// Returns a clone of the value of the wrapped Box.
64    ///
65    /// The clone is detached from any RCU read scope.
66    pub fn cloned(&self) -> T {
67        self.as_ref(&RcuReadScope::new()).clone()
68    }
69}
70
71impl<T: Send + Sync + 'static> Drop for RcuBox<T> {
72    fn drop(&mut self) {
73        // SAFETY: We can pass `std::ptr::null_mut` to `Self::replace`.
74        unsafe { self.replace(std::ptr::null_mut()) };
75    }
76}
77
78impl<T: Default + Send + Sync + 'static> Default for RcuBox<T> {
79    fn default() -> Self {
80        Self::new(T::default())
81    }
82}
83
84impl<T: Clone + Send + Sync + 'static> Clone for RcuBox<T> {
85    fn clone(&self) -> Self {
86        let value = self.read();
87        Self::new(value.clone())
88    }
89}
90
91impl<T: Send + Sync + 'static> From<Box<T>> for RcuBox<T> {
92    fn from(value: Box<T>) -> Self {
93        Self { ptr: RcuPtr::new(Box::into_raw(value)) }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use crate::state_machine::rcu_synchronize;
101    use std::ops::Deref;
102
103    #[test]
104    fn test_rcu_cell() {
105        let value = RcuBox::new(42);
106        assert_eq!(value.read().deref(), &42);
107    }
108
109    #[test]
110    fn test_rcu_cell_set_deferred() {
111        let value = RcuBox::new(42);
112        value.update(43);
113        assert_eq!(value.read().deref(), &43);
114        rcu_synchronize();
115    }
116
117    #[test]
118    fn test_rcu_cell_drop() {
119        let value = RcuBox::new(42);
120        drop(value);
121    }
122}