Skip to main content

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    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}
192
193#[cfg(test)]
194mod tests {
195    use crate::rcu_intrusive_list::{RcuListAdapter, rcu_list_adapter};
196
197    use super::*;
198    use fuchsia_rcu::rcu_run_callbacks;
199
200    #[derive(Debug)]
201    struct TestNode {
202        value: i64,
203        link: Link,
204    }
205
206    // SAFETY: TestNode does not perform any blocking or contextual work on drop.
207    unsafe impl RcuDroppable for TestNode {}
208
209    impl TestNode {
210        fn new(value: i64) -> Self {
211            Self { value, link: Default::default() }
212        }
213    }
214
215    impl RcuListAdapter<TestNode> for TestNode {
216        rcu_list_adapter!(TestNode, link);
217    }
218
219    #[test]
220    fn test_rcu_list_push_front() {
221        {
222            let list = RcuList::<TestNode, TestNode>::default();
223            let scope = RcuReadScope::new();
224            unsafe {
225                list.push_front(&scope, TestNode::new(1));
226                list.push_front(&scope, TestNode::new(2));
227                list.push_front(&scope, TestNode::new(3));
228            }
229
230            let mut cursor = list.cursor(&scope);
231            assert_eq!(cursor.current().map(|node| node.value), Some(3));
232            cursor.advance();
233            assert_eq!(cursor.current().map(|node| node.value), Some(2));
234            cursor.advance();
235            assert_eq!(cursor.current().map(|node| node.value), Some(1));
236            cursor.advance();
237            assert_eq!(cursor.current().map(|node| node.value), None);
238        }
239        rcu_run_callbacks();
240    }
241
242    #[test]
243    fn test_rcu_list_push_back() {
244        {
245            let list = RcuList::<TestNode, TestNode>::default();
246            let scope = RcuReadScope::new();
247            unsafe {
248                list.push_back(&scope, TestNode::new(1));
249                list.push_back(&scope, TestNode::new(2));
250                list.push_back(&scope, TestNode::new(3));
251            }
252
253            let mut cursor = list.cursor(&scope);
254            assert_eq!(cursor.current().map(|node| node.value), Some(1));
255            cursor.advance();
256            assert_eq!(cursor.current().map(|node| node.value), Some(2));
257            cursor.advance();
258            assert_eq!(cursor.current().map(|node| node.value), Some(3));
259            cursor.advance();
260            assert_eq!(cursor.current().map(|node| node.value), None);
261        }
262        rcu_run_callbacks();
263    }
264
265    #[test]
266    fn test_rcu_list_clear() {
267        {
268            let list = RcuList::<TestNode, TestNode>::default();
269            let scope = RcuReadScope::new();
270            unsafe {
271                list.push_back(&scope, TestNode::new(1));
272                list.push_back(&scope, TestNode::new(2));
273                list.push_back(&scope, TestNode::new(3));
274            }
275
276            unsafe { list.clear() };
277
278            let mut iter = list.iter(&scope);
279            assert_eq!(iter.next().map(|node| node.value), None);
280        }
281
282        rcu_run_callbacks();
283    }
284
285    #[test]
286    fn test_rcu_list_drop_clears_objects() {
287        use std::sync::Arc;
288        use std::sync::atomic::{AtomicUsize, Ordering};
289
290        #[derive(Debug)]
291        struct DropCounter {
292            _id: usize,
293            counter: Arc<AtomicUsize>,
294            link: Link,
295        }
296
297        // SAFETY: DropCounter only increments an atomic counter on drop.
298        unsafe impl RcuDroppable for DropCounter {}
299
300        impl RcuListAdapter<DropCounter> for DropCounter {
301            rcu_list_adapter!(DropCounter, link);
302        }
303
304        impl Drop for DropCounter {
305            fn drop(&mut self) {
306                self.counter.fetch_add(1, Ordering::SeqCst);
307            }
308        }
309
310        let drop_count = Arc::new(AtomicUsize::new(0));
311        {
312            let list = RcuList::<DropCounter, DropCounter>::default();
313            let scope = RcuReadScope::new();
314            unsafe {
315                list.push_back(
316                    &scope,
317                    DropCounter {
318                        _id: 1,
319                        counter: Arc::clone(&drop_count),
320                        link: Default::default(),
321                    },
322                );
323                list.push_back(
324                    &scope,
325                    DropCounter {
326                        _id: 2,
327                        counter: Arc::clone(&drop_count),
328                        link: Default::default(),
329                    },
330                );
331                list.push_back(
332                    &scope,
333                    DropCounter {
334                        _id: 3,
335                        counter: Arc::clone(&drop_count),
336                        link: Default::default(),
337                    },
338                );
339            }
340            assert_eq!(drop_count.load(Ordering::SeqCst), 0);
341        }
342
343        rcu_run_callbacks();
344
345        // The list is dropped here, so the contained objects should also be dropped.
346        assert_eq!(drop_count.load(Ordering::SeqCst), 3);
347    }
348
349    #[test]
350    fn test_rcu_list_iter() {
351        {
352            let list = RcuList::<TestNode, TestNode>::default();
353            let scope = RcuReadScope::new();
354            unsafe {
355                list.push_back(&scope, TestNode::new(1));
356                list.push_back(&scope, TestNode::new(2));
357                list.push_back(&scope, TestNode::new(3));
358            }
359
360            let mut iter = list.iter(&scope);
361            assert_eq!(iter.next().map(|node| node.value), Some(1));
362            assert_eq!(iter.next().map(|node| node.value), Some(2));
363            assert_eq!(iter.next().map(|node| node.value), Some(3));
364            assert_eq!(iter.next().map(|node| node.value), None);
365        }
366
367        rcu_run_callbacks();
368    }
369
370    #[test]
371    fn test_rcu_list_remove() {
372        {
373            let list = RcuList::<TestNode, TestNode>::default();
374            let scope = RcuReadScope::new();
375            unsafe {
376                list.push_back(&scope, TestNode::new(1));
377                list.push_back(&scope, TestNode::new(2));
378                list.push_back(&scope, TestNode::new(3));
379            }
380
381            let mut cursor = list.cursor(&scope);
382            cursor.advance(); // current is 2
383            assert_eq!(cursor.current().map(|node| node.value), Some(2));
384            unsafe { cursor.remove() };
385
386            let mut iter = list.iter(&scope);
387            assert_eq!(iter.next().map(|node| node.value), Some(1));
388            assert_eq!(iter.next().map(|node| node.value), Some(3));
389            assert_eq!(iter.next().map(|node| node.value), None);
390
391            // Test removing head
392            let mut cursor = list.cursor(&scope);
393            unsafe { cursor.remove() };
394
395            let mut iter = list.iter(&scope);
396            assert_eq!(iter.next().map(|node| node.value), Some(3));
397            assert_eq!(iter.next().map(|node| node.value), None);
398
399            // Test removing tail
400            let mut cursor = list.cursor(&scope);
401            unsafe { cursor.remove() };
402
403            let mut iter = list.iter(&scope);
404            assert_eq!(iter.next().map(|node| node.value), None);
405        }
406
407        rcu_run_callbacks();
408    }
409
410    #[test]
411    fn test_rcu_list_remove_all() {
412        {
413            let list = RcuList::<TestNode, TestNode>::default();
414            let scope = RcuReadScope::new();
415            unsafe {
416                list.push_back(&scope, TestNode::new(1));
417                list.push_back(&scope, TestNode::new(2));
418                list.push_back(&scope, TestNode::new(3));
419            }
420
421            let mut cursor = list.cursor(&scope);
422            while cursor.current().is_some() {
423                unsafe { cursor.remove() };
424            }
425
426            assert_eq!(list.iter(&scope).next().map(|node| node.value), None);
427        }
428
429        rcu_run_callbacks();
430    }
431
432    #[test]
433    fn test_rcu_list_append() {
434        {
435            let list1 = RcuList::<TestNode, TestNode>::default();
436            let scope = RcuReadScope::new();
437            unsafe {
438                list1.push_back(&scope, TestNode::new(1));
439                list1.push_back(&scope, TestNode::new(2));
440            }
441
442            let list2 = RcuList::<TestNode, TestNode>::default();
443            unsafe {
444                list2.push_back(&scope, TestNode::new(3));
445                list2.push_back(&scope, TestNode::new(4));
446            }
447
448            unsafe { list1.append(&scope, list2) };
449
450            let mut iter = list1.iter(&scope);
451            assert_eq!(iter.next().map(|node| node.value), Some(1));
452            assert_eq!(iter.next().map(|node| node.value), Some(2));
453            assert_eq!(iter.next().map(|node| node.value), Some(3));
454            assert_eq!(iter.next().map(|node| node.value), Some(4));
455            assert_eq!(iter.next().map(|node| node.value), None);
456        }
457
458        rcu_run_callbacks();
459    }
460
461    #[test]
462    fn test_rcu_list_append_empty() {
463        // Append to an empty list.
464        {
465            let list1 = RcuList::<TestNode, TestNode>::default();
466            let list2 = RcuList::<TestNode, TestNode>::default();
467            let scope = RcuReadScope::new();
468            unsafe {
469                list2.push_back(&scope, TestNode::new(1));
470                list2.push_back(&scope, TestNode::new(2));
471            }
472            unsafe { list1.append(&scope, list2) };
473
474            let mut iter = list1.iter(&scope);
475            assert_eq!(iter.next().map(|node| node.value), Some(1));
476            assert_eq!(iter.next().map(|node| node.value), Some(2));
477            assert_eq!(iter.next().map(|node| node.value), None);
478        }
479        rcu_run_callbacks();
480
481        // Append an empty list.
482        {
483            let list1 = RcuList::<TestNode, TestNode>::default();
484            let scope = RcuReadScope::new();
485            unsafe {
486                list1.push_back(&scope, TestNode::new(1));
487                list1.push_back(&scope, TestNode::new(2));
488            }
489            let list2 = RcuList::<TestNode, TestNode>::default();
490            unsafe { list1.append(&scope, list2) };
491
492            let mut iter = list1.iter(&scope);
493            assert_eq!(iter.next().map(|node| node.value), Some(1));
494            assert_eq!(iter.next().map(|node| node.value), Some(2));
495            assert_eq!(iter.next().map(|node| node.value), None);
496        }
497        rcu_run_callbacks();
498    }
499
500    #[test]
501    fn test_rcu_list_is_empty() {
502        {
503            let list = RcuList::<TestNode, TestNode>::default();
504            let scope = RcuReadScope::new();
505            assert!(list.is_empty());
506
507            unsafe {
508                list.push_back(&scope, TestNode::new(1));
509            }
510            assert!(!list.is_empty());
511
512            unsafe {
513                list.clear();
514            }
515            assert!(list.is_empty());
516        }
517
518        rcu_run_callbacks();
519    }
520
521    #[test]
522    fn test_rcu_list_split_off() {
523        // Split at the beginning.
524        {
525            let list = RcuList::<TestNode, TestNode>::default();
526            let scope = RcuReadScope::new();
527            unsafe {
528                list.push_back(&scope, TestNode::new(1));
529                list.push_back(&scope, TestNode::new(2));
530                list.push_back(&scope, TestNode::new(3));
531            }
532
533            let new_list = unsafe { list.split_off(&scope, 0) };
534
535            assert!(list.is_empty());
536            let mut new_iter = new_list.iter(&scope);
537            assert_eq!(new_iter.next().map(|node| node.value), Some(1));
538            assert_eq!(new_iter.next().map(|node| node.value), Some(2));
539            assert_eq!(new_iter.next().map(|node| node.value), Some(3));
540            assert_eq!(new_iter.next().map(|node| node.value), None);
541        }
542        rcu_run_callbacks();
543
544        // Split in the middle.
545        {
546            let list = RcuList::<TestNode, TestNode>::default();
547            let scope = RcuReadScope::new();
548            unsafe {
549                list.push_back(&scope, TestNode::new(1));
550                list.push_back(&scope, TestNode::new(2));
551                list.push_back(&scope, TestNode::new(3));
552                list.push_back(&scope, TestNode::new(4));
553            }
554
555            let new_list = unsafe { list.split_off(&scope, 2) };
556
557            let mut iter = list.iter(&scope);
558            assert_eq!(iter.next().map(|node| node.value), Some(1));
559            assert_eq!(iter.next().map(|node| node.value), Some(2));
560            assert_eq!(iter.next().map(|node| node.value), None);
561
562            let mut new_iter = new_list.iter(&scope);
563            assert_eq!(new_iter.next().map(|node| node.value), Some(3));
564            assert_eq!(new_iter.next().map(|node| node.value), Some(4));
565            assert_eq!(new_iter.next().map(|node| node.value), None);
566        }
567        rcu_run_callbacks();
568
569        // Split at the last element.
570        {
571            let list = RcuList::<TestNode, TestNode>::default();
572            let scope = RcuReadScope::new();
573            unsafe {
574                list.push_back(&scope, TestNode::new(1));
575                list.push_back(&scope, TestNode::new(2));
576                list.push_back(&scope, TestNode::new(3));
577            }
578
579            let new_list = unsafe { list.split_off(&scope, 2) };
580
581            let mut iter = list.iter(&scope);
582            assert_eq!(iter.next().map(|node| node.value), Some(1));
583            assert_eq!(iter.next().map(|node| node.value), Some(2));
584            assert_eq!(iter.next().map(|node| node.value), None);
585
586            let mut new_iter = new_list.iter(&scope);
587            assert_eq!(new_iter.next().map(|node| node.value), Some(3));
588            assert_eq!(new_iter.next().map(|node| node.value), None);
589        }
590        rcu_run_callbacks();
591
592        // Split one past the last element.
593        {
594            let list = RcuList::<TestNode, TestNode>::default();
595            let scope = RcuReadScope::new();
596            unsafe {
597                list.push_back(&scope, TestNode::new(1));
598                list.push_back(&scope, TestNode::new(2));
599                list.push_back(&scope, TestNode::new(3));
600            }
601
602            let new_list = unsafe { list.split_off(&scope, 3) };
603
604            let mut iter = list.iter(&scope);
605            assert_eq!(iter.next().map(|node| node.value), Some(1));
606            assert_eq!(iter.next().map(|node| node.value), Some(2));
607            assert_eq!(iter.next().map(|node| node.value), Some(3));
608            assert_eq!(iter.next().map(|node| node.value), None);
609
610            assert!(new_list.is_empty());
611        }
612        rcu_run_callbacks();
613
614        // Split far past the end of the list.
615        {
616            let list = RcuList::<TestNode, TestNode>::default();
617            let scope = RcuReadScope::new();
618            unsafe {
619                list.push_back(&scope, TestNode::new(1));
620                list.push_back(&scope, TestNode::new(2));
621                list.push_back(&scope, TestNode::new(3));
622            }
623
624            let new_list = unsafe { list.split_off(&scope, 10) };
625
626            let mut iter = list.iter(&scope);
627            assert_eq!(iter.next().map(|node| node.value), Some(1));
628            assert_eq!(iter.next().map(|node| node.value), Some(2));
629            assert_eq!(iter.next().map(|node| node.value), Some(3));
630            assert_eq!(iter.next().map(|node| node.value), None);
631
632            assert!(new_list.is_empty());
633        }
634        rcu_run_callbacks();
635    }
636}