fuchsia_rcu_collections/rcu_list.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
5#![warn(unsafe_op_in_unsafe_fn)]
6
7use fuchsia_rcu::subtle::{RcuPtr, RcuPtrRef};
8use fuchsia_rcu::{RcuDroppable, RcuReadScope, rcu_drop};
9
10use crate::rcu_intrusive_list::{Link, RcuIntrusiveList, RcuIntrusiveListCursor, RcuListAdapter};
11
12/// An `RcuList` is a doubly-linked list that supports concurrent access via
13/// read-copy-update (RCU) synchronization.
14///
15/// An `RcuList` can be safely read by multiple readers, even while a writer
16/// is modifying the list. To read from the list, you will need to enter an
17/// `RcuReadScope`.
18///
19/// To modify the list, you will need to use some external synchronization,
20/// such as a `Mutex`, to exclude concurrent writers.
21#[derive(Debug)]
22pub struct RcuList<T: RcuDroppable + Sync, A: RcuListAdapter<T>> {
23 list: RcuIntrusiveList<T, A>,
24}
25
26// SAFETY: RcuList drops all elements through its intrusive list nodes, which are of type T
27// (implementing RcuDroppable).
28unsafe impl<T: RcuDroppable + Sync, A: RcuListAdapter<T> + Send + Sync + 'static> RcuDroppable
29 for RcuList<T, A>
30{
31}
32
33impl<T: RcuDroppable + Sync, A: RcuListAdapter<T>> Default for RcuList<T, A> {
34 fn default() -> Self {
35 Self { list: RcuIntrusiveList::default() }
36 }
37}
38
39impl<T: RcuDroppable + Sync, A: RcuListAdapter<T>> RcuList<T, A> {
40 /// Creates a new list with the given head and tail.
41 pub fn new(head: RcuPtr<Link>, tail: RcuPtr<Link>) -> Self {
42 Self { list: RcuIntrusiveList::new(head, tail) }
43 }
44
45 /// Pushes a new element to the front of the list.
46 ///
47 /// # Safety
48 ///
49 /// Requires external synchronization to exclude concurrent writers.
50 pub unsafe fn push_front<'a>(&self, scope: &'a RcuReadScope, data: T) -> RcuPtrRef<'a, T> {
51 let node = alloc(scope, data);
52 // SAFETY: Our caller promises to exclude concurrent writers.
53 unsafe {
54 self.list.push_front(scope, node);
55 }
56 node
57 }
58
59 /// Pushes a new element to the back of the list.
60 ///
61 /// # Safety
62 ///
63 /// Requires external synchronization to exclude concurrent writers.
64 pub unsafe fn push_back<'a>(&self, scope: &'a RcuReadScope, data: T) -> RcuPtrRef<'a, T> {
65 let node = alloc(scope, data);
66 // SAFETY: Our caller promises to exclude concurrent writers.
67 unsafe {
68 self.list.push_back(scope, node);
69 }
70 node
71 }
72
73 /// Appends another list to the end of this list.
74 ///
75 /// # Safety
76 ///
77 /// Requires external synchronization to exclude concurrent writers.
78 pub unsafe fn append(&self, scope: &RcuReadScope, other: Self) {
79 // SAFETY: Our caller promises to exclude concurrent writers.
80 unsafe {
81 let items = other.list.split_off(scope, 0);
82 self.list.append(scope, items);
83 }
84 }
85
86 /// Splits the list into two lists at the given position.
87 ///
88 /// If the given position is past the end of the list, returns an empty list.
89 ///
90 /// # Safety
91 ///
92 /// Requires external synchronization to exclude concurrent writers.
93 pub unsafe fn split_off(&self, scope: &RcuReadScope, pos: usize) -> Self {
94 // SAFETY: Our caller promises to exclude concurrent writers.
95 Self { list: unsafe { self.list.split_off(scope, pos) } }
96 }
97
98 /// Removes all elements from the list.
99 ///
100 /// Concurrent readers may continue to see the old value of the list until the RCU state machine
101 /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
102 ///
103 /// # Safety
104 ///
105 /// Requires external synchronization to exclude concurrent writers.
106 pub unsafe fn clear(&self) {
107 let scope = RcuReadScope::new();
108 // SAFETY: Our caller promises to exclude concurrent writers.
109 unsafe { self.list.clear(&scope, deferred_dealloc) };
110 }
111
112 #[cfg(test)]
113 pub(crate) fn is_empty(&self) -> bool {
114 let scope = RcuReadScope::new();
115 self.list.is_empty(&scope)
116 }
117
118 /// Returns a cursor that can be used to traverse and modify the list.
119 ///
120 /// Concurrent readers may continue to see the old value of the list until the RCU state machine
121 /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
122 pub fn cursor<'a>(&'a self, scope: &'a RcuReadScope) -> RcuListCursor<'a, T, A> {
123 RcuListCursor { cursor: self.list.cursor(scope) }
124 }
125
126 /// Returns an iterator over the elements in the list.
127 pub fn iter<'a>(&self, scope: &'a RcuReadScope) -> impl Iterator<Item = &'a T> {
128 self.list.iter(scope)
129 }
130}
131
132/// Allocates a new node.
133///
134/// The node must be deallocated using `deferred_dealloc`.
135fn alloc<T>(scope: &RcuReadScope, data: T) -> RcuPtrRef<'_, T> {
136 let ptr = Box::into_raw(Box::new(data));
137 // SAFETY: All nodes must be deallocated using `deferred_dealloc`, which defers their
138 // deallocation until all in-flight read operations have completed.
139 unsafe { RcuPtrRef::new(scope, ptr) }
140}
141
142/// Deallocates a node once all in-flight read operations have completed.
143///
144/// The node must have been allocated using `alloc`.
145fn deferred_dealloc<T>(node: RcuPtrRef<'_, T>)
146where
147 T: RcuDroppable + Sync,
148{
149 // SAFETY: The node was allocated using `alloc`.
150 let value = unsafe { Box::from_raw(node.as_mut_ptr()) };
151 rcu_drop(value);
152}
153
154pub struct RcuListCursor<'a, T: RcuDroppable + Sync, A: RcuListAdapter<T>> {
155 cursor: RcuIntrusiveListCursor<'a, T, A>,
156}
157
158impl<'a, T: RcuDroppable + Sync, A: RcuListAdapter<T>> RcuListCursor<'a, T, A> {
159 /// Returns the element at the current cursor position.
160 pub fn current(&self) -> Option<&T> {
161 self.cursor.current()
162 }
163
164 /// Advances the cursor to the next element in the list.
165 pub fn advance(&mut self) {
166 self.cursor.advance();
167 }
168
169 /// Removes the element at the current cursor position.
170 ///
171 /// After calling `remove`, the cursor will be positioned at the next element in the list.
172 ///
173 /// Concurrent readers may continue to see this entry in the list until the RCU state machine
174 /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
175 ///
176 /// # Safety
177 ///
178 /// Requires external synchronization to exclude concurrent writers.
179 pub unsafe fn remove(&mut self) -> RcuPtrRef<'a, T> {
180 let removed = unsafe { self.cursor.remove() };
181 deferred_dealloc(removed);
182 removed
183 }
184}
185
186impl<T: RcuDroppable + Sync, A: RcuListAdapter<T>> Drop for RcuList<T, A> {
187 fn drop(&mut self) {
188 // SAFETY: The list is being dropped, so there are no concurrent readers.
189 unsafe { self.clear() };
190 }
191}