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