Skip to main content

fuchsia_rcu/
rcu_option_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 `Option<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 for some period of time.
13#[derive(Debug)]
14pub struct RcuOptionBox<T: Send + Sync + 'static> {
15    ptr: RcuPtr<T>,
16}
17
18impl<T: Send + Sync + 'static> RcuOptionBox<T> {
19    /// Create a new RCU Cell from a value.
20    pub fn new(data: Option<T>) -> Self {
21        Self::from(data.map(|data| Box::new(data)))
22    }
23
24    /// Read the value of the wrapped Box, if present.
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) -> Option<RcuReadGuard<T>> {
29        self.ptr.maybe_get()
30    }
31
32    /// Returns a reference to the value of the wrapped Box, if present.
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) -> Option<&'a T> {
37        self.ptr.read(scope).as_ref()
38    }
39
40    /// Write a new value to the RCU wrapper.
41    ///
42    /// Concurrent readers may continue to see the old value 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: Option<T>) {
45        let ptr = data.map(|data| Box::into_raw(Box::new(data))).unwrap_or(std::ptr::null_mut());
46        // SAFETY: We can pass `Box::into_raw` to `Self::replace`.
47        unsafe { self.replace(ptr) };
48    }
49
50    /// Replace the 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        if !old_ptr.is_null() {
58            // SAFETY: `old_ptr` was created by `Box::into_raw`.
59            let object = unsafe { Box::from_raw(old_ptr) };
60            rcu_drop(object);
61        }
62    }
63}
64
65impl<T: Clone + Send + Sync + 'static> RcuOptionBox<T> {
66    /// Returns a clone of the value of the wrapped Box, if present.
67    ///
68    /// The clone is detached from any RCU read scope.
69    pub fn cloned(&self) -> Option<T> {
70        self.as_ref(&RcuReadScope::new()).cloned()
71    }
72}
73
74impl<T: Send + Sync + 'static> Drop for RcuOptionBox<T> {
75    fn drop(&mut self) {
76        // SAFETY: We can pass `std::ptr::null_mut` to `Self::replace`.
77        unsafe { self.replace(std::ptr::null_mut()) };
78    }
79}
80
81impl<T: Send + Sync + 'static> Default for RcuOptionBox<T> {
82    fn default() -> Self {
83        Self::new(None)
84    }
85}
86
87impl<T: Clone + Send + Sync + 'static> Clone for RcuOptionBox<T> {
88    fn clone(&self) -> Self {
89        let value = self.read();
90        Self::new(value.map(|value| value.clone()))
91    }
92}
93
94impl<T: Send + Sync + 'static> From<Option<Box<T>>> for RcuOptionBox<T> {
95    fn from(value: Option<Box<T>>) -> Self {
96        let ptr = value.map(|value| Box::into_raw(value)).unwrap_or(std::ptr::null_mut());
97        Self { ptr: RcuPtr::new(ptr) }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use crate::state_machine::rcu_synchronize;
105    use std::ops::Deref;
106
107    #[test]
108    fn test_rcu_option_cell() {
109        let value = RcuOptionBox::new(Some(42));
110        assert_eq!(value.read().unwrap().deref(), &42);
111
112        let value = RcuOptionBox::<i32>::new(None);
113        assert!(value.read().is_none());
114    }
115
116    #[test]
117    fn test_rcu_option_cell_set_deferred() {
118        let value = RcuOptionBox::new(Some(42));
119        value.update(Some(43));
120        assert_eq!(value.read().unwrap().deref(), &43);
121
122        value.update(None);
123        assert!(value.read().is_none());
124
125        rcu_synchronize();
126        assert!(value.read().is_none());
127    }
128
129    #[test]
130    fn test_rcu_option_cell_drop() {
131        let value = RcuOptionBox::new(Some(42));
132        drop(value);
133    }
134}