Skip to main content

fuchsia_rcu_collections/
rcu_intrusive_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::RcuReadScope;
8use fuchsia_rcu::subtle::{RcuPtr, RcuPtrRef};
9
10/// `Link` is an intrusive structure in a doubly-linked list.
11///
12/// Links are address-sensitive and cannot be moved once inserted into a list.
13#[derive(Debug)]
14pub struct Link {
15    /// The next node in the list.
16    ///
17    /// This field can be used to traverse the list within an RcuReadScope.
18    next: RcuPtr<Link>,
19
20    /// The previous node in the list.
21    ///
22    /// This pointer cannot be used without external synchronization.
23    prev: RcuPtr<Link>,
24}
25
26impl Default for Link {
27    fn default() -> Self {
28        Self { next: RcuPtr::null(), prev: RcuPtr::null() }
29    }
30}
31
32/// Returns the container of a given field.
33///
34/// # Safety
35///
36/// The pointer must point to the given field in a valid instance of the container.
37#[macro_export]
38macro_rules! container_of {
39    ($ptr:expr, $container:path, $field:ident) => {{ $ptr.sub_byte_offset::<$container>(memoffset::offset_of!($container, $field)) }};
40}
41
42/// Returns the field of a given container.
43///
44/// # Safety
45///
46/// The pointer must point to a valid instance of the container.
47#[macro_export]
48macro_rules! field_of {
49    ($ptr:expr, $container:path, $field:ident, $field_type:ty) => {{ $ptr.add_byte_offset::<$field_type>(memoffset::offset_of!($container, $field)) }};
50}
51
52#[macro_export]
53macro_rules! rcu_list_adapter {
54    ($node:ty, $link:ident) => {
55        fn to_link(
56            node: fuchsia_rcu::subtle::RcuPtrRef<'_, $node>,
57        ) -> fuchsia_rcu::subtle::RcuPtrRef<'_, Link> {
58            if node.is_null() {
59                return fuchsia_rcu::subtle::RcuPtrRef::null();
60            }
61            // SAFETY: The pointer is valid and points to the given field.
62            unsafe { $crate::field_of!(node, $node, $link, Link) }
63        }
64
65        fn from_link(
66            link: fuchsia_rcu::subtle::RcuPtrRef<'_, Link>,
67        ) -> fuchsia_rcu::subtle::RcuPtrRef<'_, $node> {
68            if link.is_null() {
69                return fuchsia_rcu::subtle::RcuPtrRef::null();
70            }
71            // SAFETY: The pointer is valid and points to the given field.
72            unsafe { $crate::container_of!(link, $node, $link) }
73        }
74    };
75}
76
77pub use {container_of, field_of, rcu_list_adapter};
78
79pub trait RcuListAdapter<T> {
80    /// Returns a pointer to the Link embedded in a Node.
81    fn to_link(node: RcuPtrRef<'_, T>) -> RcuPtrRef<'_, Link>;
82
83    /// Returns a pointer to the Node containing the given Link.
84    fn from_link(link: RcuPtrRef<'_, Link>) -> RcuPtrRef<'_, T>;
85}
86
87#[derive(Debug)]
88pub struct RcuIntrusiveList<T, A: RcuListAdapter<T>> {
89    /// The first element of the list, if any.
90    ///
91    /// This field can be used to traverse the list within an RcuReadScope.
92    head: RcuPtr<Link>,
93
94    /// The last element of the list, if any.
95    ///
96    /// This pointer cannot be used without external synchronization.
97    tail: RcuPtr<Link>,
98
99    _marker: std::marker::PhantomData<(T, A)>,
100}
101
102impl<T, A: RcuListAdapter<T>> Default for RcuIntrusiveList<T, A> {
103    fn default() -> Self {
104        Self::new(RcuPtr::null(), RcuPtr::null())
105    }
106}
107
108impl<T, A: RcuListAdapter<T>> RcuIntrusiveList<T, A> {
109    /// Creates a new list with the given head and tail.
110    pub(crate) fn new(head: RcuPtr<Link>, tail: RcuPtr<Link>) -> Self {
111        Self { head, tail, _marker: std::marker::PhantomData }
112    }
113
114    /// Pushes a new element to the front of the list.
115    ///
116    /// # Safety
117    ///
118    /// Requires external synchronization to exclude concurrent writers.
119    pub unsafe fn push_front<'a>(&self, scope: &'a RcuReadScope, data: RcuPtrRef<'a, T>) {
120        let link_ptr = A::to_link(data);
121        let link = link_ptr.as_ref().unwrap();
122        let head_ptr = self.head.read(&scope);
123        if let Some(head) = head_ptr.as_ref() {
124            head.prev.assign_ptr(link_ptr);
125            link.next.assign_ptr(head_ptr);
126        } else {
127            self.tail.assign_ptr(link_ptr);
128        }
129        self.head.assign_ptr(link_ptr);
130    }
131
132    /// Pushes a new element to the back of the list.
133    ///
134    /// # Safety
135    ///
136    /// Requires external synchronization to exclude concurrent writers.
137    pub unsafe fn push_back<'a>(&self, scope: &RcuReadScope, data: RcuPtrRef<'a, T>) {
138        let link_ptr = A::to_link(data);
139        let link = link_ptr.as_ref().unwrap();
140        let tail_ptr = self.tail.read(&scope);
141        if let Some(tail) = tail_ptr.as_ref() {
142            link.prev.assign_ptr(tail_ptr);
143            tail.next.assign_ptr(link_ptr);
144        } else {
145            self.head.assign_ptr(link_ptr);
146        }
147        self.tail.assign_ptr(link_ptr);
148    }
149
150    /// Appends another list to the end of this list.
151    ///
152    /// # Safety
153    ///
154    /// Requires external synchronization to exclude concurrent writers.
155    pub unsafe fn append(&self, scope: &RcuReadScope, other: Self) {
156        let other_head_ptr = other.head.read(&scope);
157        if let Some(other_head) = other_head_ptr.as_ref() {
158            let tail_ptr = self.tail.read(&scope);
159            if let Some(tail) = tail_ptr.as_ref() {
160                tail.next.assign_ptr(other_head_ptr);
161                other_head.prev.assign_ptr(tail_ptr);
162            } else {
163                self.head.assign_ptr(other_head_ptr);
164            }
165            let other_tail_ptr = other.tail.read(&scope);
166            assert!(!other_tail_ptr.is_null());
167            self.tail.assign_ptr(other_tail_ptr);
168        }
169        other.head.assign(std::ptr::null_mut());
170        other.tail.assign(std::ptr::null_mut());
171    }
172
173    /// Removes the given node from the list.
174    ///
175    /// Returns the link of the next node in the list, if any.
176    ///
177    /// # Safety
178    ///
179    /// Requires external synchronization to exclude concurrent writers.
180    pub unsafe fn remove<'a>(
181        &self,
182        scope: &'a RcuReadScope,
183        node: RcuPtrRef<'a, T>,
184    ) -> RcuPtrRef<'a, Link> {
185        let link_ptr = A::to_link(node);
186        let link = link_ptr.as_ref().unwrap();
187
188        let prev = link.prev.read(scope);
189        let next = link.next.read(scope);
190
191        if let Some(next) = next.as_ref() {
192            next.prev.assign_ptr(prev);
193        } else {
194            self.tail.assign_ptr(prev);
195        }
196        if let Some(prev) = prev.as_ref() {
197            prev.next.assign_ptr(next);
198        } else {
199            self.head.assign_ptr(next);
200        }
201
202        // Other readers may continue to see this entry in the list and use the `next` pointer,
203        // but they should not read the `prev` pointer anymore.
204        link.prev.poison();
205
206        next
207    }
208
209    /// Splits the list into two lists at the given position.
210    ///
211    /// If the given position is past the end of the list, returns an empty list.
212    ///
213    /// # Safety
214    ///
215    /// Requires external synchronization to exclude concurrent writers.
216    pub unsafe fn split_off(&self, scope: &RcuReadScope, pos: usize) -> Self {
217        // If we're splitting at the front, just return the entire list and
218        // clear the list.
219        if pos == 0 {
220            let head = RcuPtr::new(self.head.replace(std::ptr::null_mut()));
221            let tail = RcuPtr::new(self.tail.replace(std::ptr::null_mut()));
222            return Self::new(head, tail);
223        }
224        let mut i = 1;
225        let mut prev_ptr = self.head.read(&scope);
226        while let Some(prev) = prev_ptr.as_ref() {
227            if i == pos {
228                let head = prev.next.replace(std::ptr::null_mut());
229                if head.is_null() {
230                    // There are no elements after the split point, so return an empty list.
231                    break;
232                }
233                let tail = self.tail.read(&scope);
234                self.tail.assign_ptr(prev_ptr);
235                return Self::new(RcuPtr::new(head), RcuPtr::new(tail.as_mut_ptr()));
236            }
237            prev_ptr = prev.next.read(&scope);
238            i += 1;
239        }
240        // We reached the end of the list, so return an empty list.
241        Self::default()
242    }
243
244    /// Updates the list with the contents of another list.
245    ///
246    /// # Safety
247    ///
248    /// Requires external synchronization to exclude concurrent writers.
249    pub unsafe fn update(&self, scope: &RcuReadScope, other: Self) {
250        self.head.assign_ptr(other.head.read(scope));
251        self.tail.assign_ptr(other.tail.read(scope));
252    }
253
254    /// Removes all elements from the list.
255    ///
256    /// The callback is called for each element in the list. The caller is responsible for cleaning
257    /// up the removed elements.
258    ///
259    /// Concurrent readers may continue to see the old value of the list until the RCU state machine
260    /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
261    ///
262    /// # Safety
263    ///
264    /// Requires external synchronization to exclude concurrent writers.
265    pub unsafe fn clear<'a>(&self, scope: &'a RcuReadScope, callback: impl Fn(RcuPtrRef<'a, T>))
266    where
267        T: 'static,
268    {
269        let mut current = self.head.read(scope);
270
271        self.head.assign(std::ptr::null_mut());
272        self.tail.assign(std::ptr::null_mut());
273
274        while let Some(link) = current.as_ref() {
275            let next = link.next.read(scope);
276
277            // Other readers may continue to see this entry in the list and use the `next` pointer,
278            // but they should not read the `prev` pointer anymore.
279            link.prev.poison();
280            callback(A::from_link(current));
281            current = next;
282        }
283    }
284
285    #[cfg(test)]
286    pub(crate) fn is_empty(&self, scope: &RcuReadScope) -> bool {
287        self.head.read(scope).is_null()
288    }
289
290    /// Returns a cursor that can be used to traverse and modify the list.
291    ///
292    /// Concurrent readers may continue to see the old value of the list until the RCU state machine
293    /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
294    pub fn cursor<'a>(&'a self, scope: &'a RcuReadScope) -> RcuIntrusiveListCursor<'a, T, A> {
295        let current = self.head.read(scope);
296        RcuIntrusiveListCursor { scope, list: self, current }
297    }
298
299    /// Returns an iterator over the elements in the list.
300    pub fn iter<'a>(&self, scope: &'a RcuReadScope) -> impl Iterator<Item = &'a T>
301    where
302        T: 'static,
303    {
304        let next = self.head.read(&scope);
305        RcuIntrusiveListIter::<T, A> { scope, next, _marker: std::marker::PhantomData }
306    }
307}
308
309/// A cursor for traversing and modifying an `RcuList`.
310///
311/// See `RcuList::cursor` for more information.
312pub struct RcuIntrusiveListCursor<'a, T, A: RcuListAdapter<T>> {
313    scope: &'a RcuReadScope,
314    list: &'a RcuIntrusiveList<T, A>,
315    current: RcuPtrRef<'a, Link>,
316}
317
318impl<'a, T, A: RcuListAdapter<T>> RcuIntrusiveListCursor<'a, T, A> {
319    /// Returns the element at the current cursor position.
320    pub fn current(&self) -> Option<&'a T> {
321        let node = A::from_link(self.current);
322        node.as_ref()
323    }
324
325    /// Advances the cursor to the next element in the list.
326    pub fn advance(&mut self) {
327        if let Some(link) = self.current.as_ref() {
328            self.current = link.next.read(&self.scope);
329        }
330    }
331
332    /// Removes the element at the current cursor position.
333    ///
334    /// After calling `remove`, the cursor will be positioned at the next element in the list.
335    ///
336    /// Returns a pointer to the removed element. The caller is responsible for cleaning up the
337    /// removed element.
338    ///
339    /// Concurrent readers may continue to see this entry in the list until the RCU state machine
340    /// has made sufficient progress to ensure that no concurrent readers are holding read guards.
341    ///
342    /// # Safety
343    ///
344    /// Requires external synchronization to exclude concurrent writers.
345    pub unsafe fn remove(&mut self) -> RcuPtrRef<'a, T> {
346        if self.current.is_null() {
347            return RcuPtrRef::null();
348        }
349        let removed_node = A::from_link(self.current);
350        // SAFETY: The caller promises to exclude concurrent writers.
351        unsafe {
352            self.current = self.list.remove(&self.scope, removed_node);
353        }
354        removed_node
355    }
356}
357
358struct RcuIntrusiveListIter<'a, T, A: RcuListAdapter<T>> {
359    scope: &'a RcuReadScope,
360    next: RcuPtrRef<'a, Link>,
361    _marker: std::marker::PhantomData<(T, A)>,
362}
363
364impl<'a, T: 'static, A: RcuListAdapter<T>> Iterator for RcuIntrusiveListIter<'a, T, A> {
365    type Item = &'a T;
366
367    fn next(&mut self) -> Option<Self::Item> {
368        if let Some(link) = self.next.as_ref() {
369            let current = self.next;
370            self.next = link.next.read(&self.scope);
371            Some(A::from_link(current).as_ref().unwrap())
372        } else {
373            None
374        }
375    }
376}