Skip to main content

fuchsia_rcu_collections/
rcu_array.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 fuchsia_rcu::{RcuCell, RcuReadScope};
6
7/// An array-like data structure that can be read without locking.
8///
9/// `RcuArray` provides a way to share a growable array between multiple threads.
10/// Writers create a new copy of the array when it needs to be grown, and readers
11/// are guaranteed to see a consistent snapshot of the array without blocking
12/// writers.
13#[derive(Default, Debug)]
14pub struct RcuArray<T: Send + Sync + 'static> {
15    inner: RcuCell<Box<[T]>>,
16}
17
18impl<T: Send + Sync + 'static> RcuArray<T> {
19    /// Returns a reference to the element at the given `index`, or `None` if the
20    /// index is out of bounds.
21    pub fn get<'a>(&self, scope: &'a RcuReadScope, index: usize) -> Option<&'a T> {
22        let array = self.inner.as_ref(scope);
23        array.get(index)
24    }
25
26    /// Returns a slice containing the entire array.
27    pub fn as_slice<'a>(&self, scope: &'a RcuReadScope) -> &'a [T] {
28        let array = self.inner.as_ref(scope);
29        array.as_ref()
30    }
31
32    /// Ensures that the array has at least `requested_size` elements, filling with
33    /// `value` if the array needs to be grown.
34    ///
35    /// If the array is already large enough, this function does nothing. Otherwise,
36    /// the array is grown to at least `requested_size`. To avoid frequent reallocations,
37    /// the array will at least double in size.
38    ///
39    /// # Safety
40    ///
41    /// Requires external synchronization to exclude concurrent writers.
42    pub unsafe fn ensure_at_least(&self, requested_size: usize)
43    where
44        T: Clone + Default,
45    {
46        let array = self.inner.read();
47        if array.len() >= requested_size {
48            return;
49        }
50        let new_size = std::cmp::max(requested_size, array.len() * 2);
51        self.copy_update(&array, new_size);
52    }
53
54    /// Updates the array to contain the given vector.
55    pub fn update(&self, new_array: Vec<T>) {
56        self.inner.update(new_array.into_boxed_slice());
57    }
58
59    fn copy_update(&self, array: &[T], new_size: usize)
60    where
61        T: Clone + Default,
62    {
63        let mut new_array = Vec::new();
64        new_array.reserve_exact(new_size);
65        for item in array.iter() {
66            new_array.push(item.clone());
67        }
68        for _ in array.len()..new_size {
69            new_array.push(T::default());
70        }
71        self.inner.update(new_array.into_boxed_slice());
72    }
73}
74
75impl<T: Clone + Sync + Send + 'static> Clone for RcuArray<T> {
76    fn clone(&self) -> Self {
77        Self { inner: self.inner.clone() }
78    }
79}
80
81/// Creates an `RcuArray` from a `Vec<T>`.
82impl<T: Send + Sync + 'static> From<Vec<T>> for RcuArray<T> {
83    fn from(value: Vec<T>) -> Self {
84        Self { inner: RcuCell::new(value.into_boxed_slice()) }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use fuchsia_rcu::{RcuReadScope, rcu_synchronize};
92
93    #[test]
94    fn test_rcu_array_get() {
95        let array = RcuArray::from(vec![1, 2, 3]);
96        let scope = RcuReadScope::new();
97        assert_eq!(array.get(&scope, 0), Some(&1));
98        assert_eq!(array.get(&scope, 1), Some(&2));
99        assert_eq!(array.get(&scope, 2), Some(&3));
100        assert_eq!(array.get(&scope, 3), None);
101    }
102
103    #[test]
104    fn test_rcu_array_as_slice() {
105        let array = RcuArray::from(vec![1, 2, 3]);
106        let scope = RcuReadScope::new();
107        assert_eq!(array.as_slice(&scope), &[1, 2, 3]);
108    }
109
110    #[test]
111    fn test_rcu_array_ensure_at_least() {
112        let array = RcuArray::from(vec![1, 2, 3]);
113
114        unsafe { array.ensure_at_least(5) };
115        let scope = RcuReadScope::new();
116        // Should at least double.
117        assert_eq!(array.as_slice(&scope), &[1, 2, 3, 0, 0, 0]);
118
119        unsafe { array.ensure_at_least(2) };
120
121        // Should not shrink below current size.
122        assert_eq!(array.as_slice(&scope), &[1, 2, 3, 0, 0, 0]);
123
124        unsafe { array.ensure_at_least(12) };
125        assert_eq!(array.as_slice(&scope), &[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
126
127        std::mem::drop(scope);
128        rcu_synchronize();
129    }
130
131    #[test]
132    fn test_rcu_array_from_vec() {
133        let vec = vec![1, 2, 3];
134        let array = RcuArray::from(vec.clone());
135        let scope = RcuReadScope::new();
136        assert_eq!(array.as_slice(&scope), vec.as_slice());
137    }
138}