Skip to main content

cu29_runtime/
copperlist.rs

1//! CopperList is the main data structure used by Copper to communicate between tasks.
2//! It is a queue that can be used to store preallocated messages between tasks in memory order.
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8
9use bincode::{Decode, Encode};
10use core::fmt;
11
12use core::fmt::Display;
13use core::iter::{Chain, Rev};
14use core::slice::{Iter as SliceIter, IterMut as SliceIterMut};
15use cu29_traits::{CopperListTuple, ErasedCuStampedData, ErasedCuStampedDataSet};
16use serde_derive::{Deserialize, Serialize};
17
18const MAX_TASKS: usize = 512;
19
20/// Not implemented yet.
21/// This mask will be used to for example filter out necessary regions of a copper list between remote systems.
22#[derive(Debug, Encode, Decode, PartialEq, Clone, Copy)]
23pub struct CopperLiskMask {
24    #[allow(dead_code)]
25    mask: [u128; MAX_TASKS / 128 + 1],
26}
27
28/// Those are the possible states along the lifetime of a CopperList.
29#[derive(Debug, Encode, Decode, Serialize, Deserialize, PartialEq, Copy, Clone)]
30pub enum CopperListState {
31    Free,
32    Initialized,
33    Processing,
34    DoneProcessing,
35    QueuedForSerialization,
36    BeingSerialized,
37}
38
39impl Display for CopperListState {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            CopperListState::Free => write!(f, "Free"),
43            CopperListState::Initialized => write!(f, "Initialized"),
44            CopperListState::Processing => write!(f, "Processing"),
45            CopperListState::DoneProcessing => write!(f, "DoneProcessing"),
46            CopperListState::QueuedForSerialization => write!(f, "QueuedForSerialization"),
47            CopperListState::BeingSerialized => write!(f, "BeingSerialized"),
48        }
49    }
50}
51
52#[derive(Debug, Encode, Decode, Serialize, Deserialize)]
53pub struct CopperList<P: CopperListTuple> {
54    pub id: u64,
55    state: CopperListState,
56    pub msgs: P, // This is generated from the runtime.
57}
58
59impl<P: CopperListTuple> Default for CopperList<P> {
60    fn default() -> Self {
61        CopperList {
62            id: 0,
63            state: CopperListState::Free,
64            msgs: P::default(),
65        }
66    }
67}
68
69impl<P: CopperListTuple> CopperList<P> {
70    // This is not the usual way to create a CopperList, this is just for testing.
71    pub fn new(id: u64, msgs: P) -> Self {
72        CopperList {
73            id,
74            state: CopperListState::Initialized,
75            msgs,
76        }
77    }
78
79    pub fn change_state(&mut self, new_state: CopperListState) {
80        self.state = new_state; // TODO: probably wise here to enforce a state machine.
81    }
82
83    pub fn get_state(&self) -> CopperListState {
84        self.state
85    }
86
87    /// Restores the lifecycle state and per-cycle metadata of an initialized slot.
88    /// Existing payloads remain available for reuse by tasks.
89    #[doc(hidden)]
90    pub fn reset_for_runtime_use(&mut self, id: u64)
91    where
92        P: CuListZeroedInit,
93    {
94        self.id = id;
95        self.state = CopperListState::Initialized;
96        self.msgs.init_zeroed();
97    }
98
99    /// Initializes a pool slot directly in its allocated storage.
100    ///
101    /// # Safety
102    /// `dst` must point to aligned, writable storage for one `Self`. Its previous
103    /// contents are overwritten without being dropped.
104    pub(crate) unsafe fn init_in_place(dst: *mut Self)
105    where
106        P: CuListZeroedInit,
107    {
108        // SAFETY: The caller supplies storage for every field. No reference to
109        // the CopperList is formed before its message dataset is initialized.
110        unsafe {
111            core::ptr::addr_of_mut!((*dst).id).write(0);
112            core::ptr::addr_of_mut!((*dst).state).write(CopperListState::Free);
113            let msgs = core::ptr::addr_of_mut!((*dst).msgs);
114            let initialized = P::init_in_place(&mut *msgs.cast::<core::mem::MaybeUninit<P>>());
115            assert!(
116                core::ptr::eq(initialized, msgs),
117                "initializer returned a different slot"
118            );
119        }
120    }
121}
122
123impl<P: CopperListTuple> ErasedCuStampedDataSet for CopperList<P> {
124    fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
125        self.msgs.cumsgs()
126    }
127}
128
129/// This structure maintains the entire memory needed by Copper for one loop for the inter tasks communication within a process.
130/// P or Payload is typically a Tuple of various types of messages that are exchanged between tasks.
131/// N is the maximum number of in flight Copper List the runtime can support.
132pub struct CuListsManager<P: CopperListTuple, const N: usize> {
133    data: Box<[CopperList<P>; N]>,
134    length: usize,
135    insertion_index: usize,
136    current_cl_id: u64,
137}
138
139impl<P: CopperListTuple + fmt::Debug, const N: usize> fmt::Debug for CuListsManager<P, N> {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        f.debug_struct("CuListsManager")
142            .field("data", &self.data)
143            .field("length", &self.length)
144            .field("insertion_index", &self.insertion_index)
145            // Do not include on_drop field
146            .finish()
147    }
148}
149
150pub type Iter<'a, T> = Chain<Rev<SliceIter<'a, T>>, Rev<SliceIter<'a, T>>>;
151pub type IterMut<'a, T> = Chain<Rev<SliceIterMut<'a, T>>, Rev<SliceIterMut<'a, T>>>;
152pub type AscIter<'a, T> = Chain<SliceIter<'a, T>, SliceIter<'a, T>>;
153pub type AscIterMut<'a, T> = Chain<SliceIterMut<'a, T>, SliceIterMut<'a, T>>;
154
155/// Initializes CopperList storage and resets per-cycle metadata on reuse.
156///
157/// Pool slots contain valid values before the runtime borrows them.
158///
159/// Existing implementations use `Default` for startup initialization.
160pub trait CuListZeroedInit: CopperListTuple {
161    /// Resets per-cycle metadata on an already initialized dataset.
162    fn init_zeroed(&mut self);
163
164    /// Constructs a valid dataset in its pool storage, once at startup.
165    /// Generated datasets override this to initialize messages individually.
166    ///
167    /// Returns the initialized value in `dst`. The pool checks that the returned
168    /// reference points to the supplied slot before exposing or dropping it.
169    #[doc(hidden)]
170    fn init_in_place(dst: &mut core::mem::MaybeUninit<Self>) -> &mut Self {
171        dst.write(Self::default())
172    }
173}
174
175impl<P: CopperListTuple + CuListZeroedInit, const N: usize> Default for CuListsManager<P, N> {
176    fn default() -> Self {
177        Self::new()
178    }
179}
180
181impl<P: CopperListTuple, const N: usize> CuListsManager<P, N> {
182    pub fn new() -> Self
183    where
184        P: CuListZeroedInit,
185    {
186        let mut slots = Vec::<CopperList<P>>::with_capacity(N);
187        for index in 0..N {
188            // SAFETY: Capacity is N and this slot has not been initialized yet.
189            // Advancing the length afterwards also makes Vec drop all completed
190            // slots if initialization of a later slot unwinds.
191            unsafe {
192                CopperList::init_in_place(slots.as_mut_ptr().add(index));
193                slots.set_len(index + 1);
194            }
195        }
196        // SAFETY: Exactly N initialized slots were placed in the boxed slice.
197        let data = unsafe {
198            Box::from_raw(Box::into_raw(slots.into_boxed_slice()) as *mut [CopperList<P>; N])
199        };
200        CuListsManager {
201            data,
202            length: 0,
203            insertion_index: 0,
204            current_cl_id: 0,
205        }
206    }
207
208    /// Returns the current number of elements in the queue.
209    ///
210    #[inline]
211    pub fn len(&self) -> usize {
212        self.length
213    }
214
215    /// Returns `true` if the queue contains no elements.
216    ///
217    #[inline]
218    pub fn is_empty(&self) -> bool {
219        self.length == 0
220    }
221
222    /// Returns `true` if the queue is full.
223    ///
224    #[inline]
225    pub fn is_full(&self) -> bool {
226        N == self.len()
227    }
228
229    /// Clears the queue.
230    ///
231    #[inline]
232    pub fn clear(&mut self) {
233        self.insertion_index = 0;
234        self.length = 0;
235    }
236
237    #[inline]
238    pub fn create(&mut self) -> Option<&mut CopperList<P>>
239    where
240        P: CuListZeroedInit,
241    {
242        if self.is_full() {
243            return None;
244        }
245        let next_id = self.current_cl_id;
246        let result = &mut self.data[self.insertion_index];
247        self.insertion_index = (self.insertion_index + 1) % N;
248        self.length += 1;
249
250        // We assign a unique id to each CopperList to be able to track them across their lifetime.
251        result.reset_for_runtime_use(next_id);
252        self.current_cl_id += 1;
253
254        Some(result)
255    }
256
257    /// Returns the next copper-list id that will be assigned by [`create`](Self::create).
258    #[inline]
259    pub fn next_cl_id(&self) -> u64 {
260        self.current_cl_id
261    }
262
263    /// Returns the most recently assigned copper-list id.
264    ///
265    /// Before the first call to [`create`](Self::create), this returns `0`.
266    #[inline]
267    pub fn last_cl_id(&self) -> u64 {
268        self.current_cl_id.saturating_sub(1)
269    }
270
271    /// Peeks at the last element in the queue.
272    #[inline]
273    pub fn peek(&self) -> Option<&CopperList<P>> {
274        if self.length == 0 {
275            return None;
276        }
277        let index = if self.insertion_index == 0 {
278            N - 1
279        } else {
280            self.insertion_index - 1
281        };
282        Some(&self.data[index])
283    }
284
285    #[inline]
286    #[allow(dead_code)]
287    fn drop_last(&mut self) {
288        if self.length == 0 {
289            return;
290        }
291        if self.insertion_index == 0 {
292            self.insertion_index = N - 1;
293        } else {
294            self.insertion_index -= 1;
295        }
296        self.length -= 1;
297    }
298
299    #[inline]
300    pub fn pop(&mut self) -> Option<&mut CopperList<P>> {
301        if self.length == 0 {
302            return None;
303        }
304        if self.insertion_index == 0 {
305            self.insertion_index = N - 1;
306        } else {
307            self.insertion_index -= 1;
308        }
309        self.length -= 1;
310        Some(&mut self.data[self.insertion_index])
311    }
312
313    /// Returns an iterator over the queue's contents.
314    ///
315    /// The iterator goes from the most recently pushed items to the oldest ones.
316    ///
317    #[inline]
318    pub fn iter(&self) -> Iter<'_, CopperList<P>> {
319        let (a, b) = self.data[0..self.length].split_at(self.insertion_index);
320        a.iter().rev().chain(b.iter().rev())
321    }
322
323    /// Returns a mutable iterator over the queue's contents.
324    ///
325    /// The iterator goes from the most recently pushed items to the oldest ones.
326    ///
327    #[inline]
328    pub fn iter_mut(&mut self) -> IterMut<'_, CopperList<P>> {
329        let (a, b) = self.data[0..self.length].split_at_mut(self.insertion_index);
330        a.iter_mut().rev().chain(b.iter_mut().rev())
331    }
332
333    /// Returns an ascending iterator over the queue's contents.
334    ///
335    /// The iterator goes from the least recently pushed items to the newest ones.
336    ///
337    #[inline]
338    pub fn asc_iter(&self) -> AscIter<'_, CopperList<P>> {
339        let (a, b) = self.data[0..self.length].split_at(self.insertion_index);
340        b.iter().chain(a.iter())
341    }
342
343    /// Returns a mutable ascending iterator over the queue's contents.
344    ///
345    /// The iterator goes from the least recently pushed items to the newest ones.
346    ///
347    #[inline]
348    pub fn asc_iter_mut(&mut self) -> AscIterMut<'_, CopperList<P>> {
349        let (a, b) = self.data[0..self.length].split_at_mut(self.insertion_index);
350        b.iter_mut().chain(a.iter_mut())
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    use cu29_traits::{ErasedCuStampedData, ErasedCuStampedDataSet, MatchingTasks};
358    use serde::{Deserialize, Serialize, Serializer};
359
360    #[derive(Debug, Encode, Decode, PartialEq, Clone, Copy, Serialize, Deserialize, Default)]
361    struct CuStampedDataSet(i32);
362
363    impl ErasedCuStampedDataSet for CuStampedDataSet {
364        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
365            Vec::new()
366        }
367    }
368
369    impl MatchingTasks for CuStampedDataSet {
370        fn get_all_task_ids() -> &'static [&'static str] {
371            &[]
372        }
373    }
374
375    impl CuListZeroedInit for CuStampedDataSet {
376        fn init_zeroed(&mut self) {}
377    }
378
379    #[derive(Debug, Default, Encode, Decode, Serialize, Deserialize)]
380    struct WrongSlot;
381
382    impl ErasedCuStampedDataSet for WrongSlot {
383        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
384            Vec::new()
385        }
386    }
387
388    impl MatchingTasks for WrongSlot {
389        fn get_all_task_ids() -> &'static [&'static str] {
390            &[]
391        }
392    }
393
394    impl CuListZeroedInit for WrongSlot {
395        fn init_zeroed(&mut self) {}
396
397        fn init_in_place(_dst: &mut core::mem::MaybeUninit<Self>) -> &mut Self {
398            Box::leak(Box::new(Self))
399        }
400    }
401
402    #[test]
403    #[should_panic(expected = "initializer returned a different slot")]
404    fn rejects_initializer_returning_another_slot() {
405        let _ = CuListsManager::<WrongSlot, 1>::new();
406    }
407
408    #[test]
409    fn empty_queue() {
410        let q = CuListsManager::<CuStampedDataSet, 5>::new();
411
412        assert!(q.is_empty());
413        assert!(q.iter().next().is_none());
414        assert!(q.asc_iter().next().is_none());
415        assert!(q.peek().is_none());
416    }
417
418    #[test]
419    fn partially_full_queue() {
420        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
421        q.create().unwrap().msgs.0 = 1;
422        q.create().unwrap().msgs.0 = 2;
423        q.create().unwrap().msgs.0 = 3;
424
425        assert!(!q.is_empty());
426        assert_eq!(q.len(), 3);
427
428        let res: Vec<i32> = q.iter().map(|x| x.msgs.0).collect();
429        assert_eq!(res, [3, 2, 1]);
430    }
431
432    #[test]
433    fn full_queue() {
434        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
435        q.create().unwrap().msgs.0 = 1;
436        q.create().unwrap().msgs.0 = 2;
437        q.create().unwrap().msgs.0 = 3;
438        q.create().unwrap().msgs.0 = 4;
439        q.create().unwrap().msgs.0 = 5;
440        assert_eq!(q.len(), 5);
441
442        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
443        assert_eq!(res, [5, 4, 3, 2, 1]);
444    }
445
446    #[test]
447    fn over_full_queue() {
448        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
449        q.create().unwrap().msgs.0 = 1;
450        q.create().unwrap().msgs.0 = 2;
451        q.create().unwrap().msgs.0 = 3;
452        q.create().unwrap().msgs.0 = 4;
453        q.create().unwrap().msgs.0 = 5;
454        assert!(q.create().is_none());
455        assert_eq!(q.len(), 5);
456
457        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
458        assert_eq!(res, [5, 4, 3, 2, 1]);
459    }
460
461    #[test]
462    fn clear() {
463        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
464        q.create().unwrap().msgs.0 = 1;
465        q.create().unwrap().msgs.0 = 2;
466        q.create().unwrap().msgs.0 = 3;
467        q.create().unwrap().msgs.0 = 4;
468        q.create().unwrap().msgs.0 = 5;
469        assert!(q.create().is_none());
470        assert_eq!(q.len(), 5);
471
472        q.clear();
473
474        assert_eq!(q.len(), 0);
475        assert!(q.iter().next().is_none());
476
477        q.create().unwrap().msgs.0 = 1;
478        q.create().unwrap().msgs.0 = 2;
479        q.create().unwrap().msgs.0 = 3;
480
481        assert_eq!(q.len(), 3);
482
483        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
484        assert_eq!(res, [3, 2, 1]);
485    }
486
487    #[test]
488    fn create_fresh_slot_starts_initialized_with_zeroed_payload() {
489        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
490
491        let cl = q.create().unwrap();
492        assert_eq!(cl.id, 0);
493        assert_eq!(cl.get_state(), CopperListState::Initialized);
494        assert_eq!(cl.msgs.0, 0);
495        assert_eq!(q.next_cl_id(), 1);
496        assert_eq!(q.last_cl_id(), 0);
497    }
498
499    #[test]
500    fn create_reused_slot_reinitializes_state_but_preserves_payload_storage() {
501        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
502
503        {
504            let cl = q.create().unwrap();
505            cl.msgs.0 = 41;
506            cl.change_state(CopperListState::Processing);
507        }
508
509        let popped = q.pop().unwrap();
510        assert_eq!(popped.id, 0);
511        assert_eq!(popped.get_state(), CopperListState::Processing);
512        assert_eq!(popped.msgs.0, 41);
513
514        let reused = q.create().unwrap();
515        assert_eq!(reused.id, 1);
516        assert_eq!(reused.get_state(), CopperListState::Initialized);
517        assert_eq!(reused.msgs.0, 41);
518        assert_eq!(q.next_cl_id(), 2);
519        assert_eq!(q.last_cl_id(), 1);
520    }
521
522    #[test]
523    fn mutable_iterator() {
524        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
525        q.create().unwrap().msgs.0 = 1;
526        q.create().unwrap().msgs.0 = 2;
527        q.create().unwrap().msgs.0 = 3;
528        q.create().unwrap().msgs.0 = 4;
529        q.create().unwrap().msgs.0 = 5;
530
531        for x in q.iter_mut() {
532            x.msgs.0 *= 2;
533        }
534
535        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
536        assert_eq!(res, [10, 8, 6, 4, 2]);
537    }
538
539    #[test]
540    fn mutable_iterator_non_wrapped_only_visits_active_slots() {
541        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
542        q.create().unwrap().msgs.0 = 1;
543        q.create().unwrap().msgs.0 = 2;
544        q.create().unwrap().msgs.0 = 3;
545
546        let mut visited = Vec::new();
547        for cl in q.iter_mut() {
548            visited.push(cl.id);
549            cl.msgs.0 *= 10;
550        }
551
552        assert_eq!(visited, vec![2, 1, 0]);
553        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
554        assert_eq!(res, [30, 20, 10]);
555    }
556
557    #[test]
558    fn test_drop_last() {
559        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
560        q.create().unwrap().msgs.0 = 1;
561        q.create().unwrap().msgs.0 = 2;
562        q.create().unwrap().msgs.0 = 3;
563        q.create().unwrap().msgs.0 = 4;
564        q.create().unwrap().msgs.0 = 5;
565        assert_eq!(q.len(), 5);
566
567        q.drop_last();
568        assert_eq!(q.len(), 4);
569
570        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
571        assert_eq!(res, [4, 3, 2, 1]);
572    }
573
574    #[test]
575    fn drop_last_on_empty_queue_is_a_noop() {
576        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
577
578        q.drop_last();
579
580        assert!(q.is_empty());
581        assert!(q.peek().is_none());
582        assert!(q.pop().is_none());
583    }
584
585    #[test]
586    fn drop_last_on_non_wrapped_queue_removes_most_recent_slot() {
587        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
588        q.create().unwrap().msgs.0 = 1;
589        q.create().unwrap().msgs.0 = 2;
590        q.create().unwrap().msgs.0 = 3;
591
592        q.drop_last();
593
594        assert_eq!(q.len(), 2);
595        assert_eq!(q.peek().unwrap().msgs.0, 2);
596        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
597        assert_eq!(res, [2, 1]);
598    }
599
600    #[test]
601    fn test_pop() {
602        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
603        q.create().unwrap().msgs.0 = 1;
604        q.create().unwrap().msgs.0 = 2;
605        q.create().unwrap().msgs.0 = 3;
606        q.create().unwrap().msgs.0 = 4;
607        q.create().unwrap().msgs.0 = 5;
608        assert_eq!(q.len(), 5);
609
610        let last = q.pop().unwrap();
611        assert_eq!(last.msgs.0, 5);
612        assert_eq!(q.len(), 4);
613
614        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
615        assert_eq!(res, [4, 3, 2, 1]);
616    }
617
618    #[test]
619    fn pop_on_empty_queue_returns_none() {
620        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
621
622        assert!(q.pop().is_none());
623        assert!(q.is_empty());
624    }
625
626    #[test]
627    fn test_peek() {
628        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
629        q.create().unwrap().msgs.0 = 1;
630        q.create().unwrap().msgs.0 = 2;
631        q.create().unwrap().msgs.0 = 3;
632        q.create().unwrap().msgs.0 = 4;
633        q.create().unwrap().msgs.0 = 5;
634        assert_eq!(q.len(), 5);
635
636        let last = q.peek().unwrap();
637        assert_eq!(last.msgs.0, 5);
638        assert_eq!(q.len(), 5);
639
640        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
641        assert_eq!(res, [5, 4, 3, 2, 1]);
642    }
643
644    #[test]
645    fn peek_on_empty_queue_returns_none() {
646        let q = CuListsManager::<CuStampedDataSet, 5>::new();
647
648        assert!(q.peek().is_none());
649    }
650
651    #[test]
652    fn next_and_last_cl_id_track_assigned_ids() {
653        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
654
655        // Before first allocation, next id is 0 and last id saturates to 0.
656        assert_eq!(q.next_cl_id(), 0);
657        assert_eq!(q.last_cl_id(), 0);
658
659        let cl0 = q.create().unwrap();
660        assert_eq!(cl0.id, 0);
661        assert_eq!(q.next_cl_id(), 1);
662        assert_eq!(q.last_cl_id(), 0);
663
664        let cl1 = q.create().unwrap();
665        assert_eq!(cl1.id, 1);
666        assert_eq!(q.next_cl_id(), 2);
667        assert_eq!(q.last_cl_id(), 1);
668
669        let _ = q.pop().unwrap();
670        let cl2 = q.create().unwrap();
671        assert_eq!(cl2.id, 2);
672        assert_eq!(q.next_cl_id(), 3);
673        assert_eq!(q.last_cl_id(), 2);
674    }
675
676    #[test]
677    fn asc_iter_non_wrapped_returns_oldest_to_newest_without_free_slots() {
678        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
679        q.create().unwrap().msgs.0 = 10;
680        q.create().unwrap().msgs.0 = 20;
681        q.create().unwrap().msgs.0 = 30;
682
683        let res: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
684        assert_eq!(res, [10, 20, 30]);
685    }
686
687    #[test]
688    fn asc_iter_mut_non_wrapped_only_visits_active_slots_in_oldest_first_order() {
689        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
690        q.create().unwrap().msgs.0 = 10;
691        q.create().unwrap().msgs.0 = 20;
692        q.create().unwrap().msgs.0 = 30;
693
694        let mut visited = Vec::new();
695        for (offset, cl) in q.asc_iter_mut().enumerate() {
696            visited.push(cl.id);
697            cl.msgs.0 += offset as i32;
698        }
699
700        assert_eq!(visited, vec![0, 1, 2]);
701        let res: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
702        assert_eq!(res, [10, 21, 32]);
703    }
704
705    #[test]
706    fn asc_iter_wrapped_layout_tracks_reused_slots_in_ascending_order() {
707        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
708        for value in 1..=5 {
709            q.create().unwrap().msgs.0 = value;
710        }
711        assert_eq!(q.pop().unwrap().msgs.0, 5);
712        assert_eq!(q.pop().unwrap().msgs.0, 4);
713        q.create().unwrap().msgs.0 = 6;
714        q.create().unwrap().msgs.0 = 7;
715
716        let desc: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
717        assert_eq!(desc, [7, 6, 3, 2, 1]);
718
719        let asc: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
720        assert_eq!(asc, [1, 2, 3, 6, 7]);
721    }
722
723    #[test]
724    fn asc_iter_mut_wrapped_layout_updates_oldest_to_newest_order() {
725        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
726        for value in 1..=5 {
727            q.create().unwrap().msgs.0 = value;
728        }
729        let _ = q.pop().unwrap();
730        let _ = q.pop().unwrap();
731        q.create().unwrap().msgs.0 = 6;
732        q.create().unwrap().msgs.0 = 7;
733
734        let mut visited = Vec::new();
735        for (offset, cl) in q.asc_iter_mut().enumerate() {
736            visited.push(cl.id);
737            cl.msgs.0 += offset as i32;
738        }
739
740        assert_eq!(visited, vec![0, 1, 2, 5, 6]);
741        let asc: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
742        assert_eq!(asc, [1, 3, 5, 9, 11]);
743    }
744
745    #[derive(Decode, Encode, Debug, PartialEq, Clone, Copy)]
746    struct TestStruct {
747        content: [u8; 10_000_000],
748    }
749
750    impl Default for TestStruct {
751        fn default() -> Self {
752            TestStruct {
753                content: [0; 10_000_000],
754            }
755        }
756    }
757
758    impl ErasedCuStampedDataSet for TestStruct {
759        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
760            Vec::new()
761        }
762    }
763
764    impl Serialize for TestStruct {
765        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
766        where
767            S: Serializer,
768        {
769            serializer.serialize_i8(0)
770        }
771    }
772
773    impl MatchingTasks for TestStruct {
774        fn get_all_task_ids() -> &'static [&'static str] {
775            &[]
776        }
777    }
778
779    impl CuListZeroedInit for TestStruct {
780        fn init_zeroed(&mut self) {}
781
782        fn init_in_place(dst: &mut core::mem::MaybeUninit<Self>) -> &mut Self {
783            // SAFETY: TestStruct contains only bytes, all initialized to zero.
784            unsafe {
785                dst.as_mut_ptr().write_bytes(0, 1);
786                dst.assume_init_mut()
787            }
788        }
789    }
790
791    #[test]
792    fn be_sure_we_wont_stackoverflow_at_init() {
793        let _ = CuListsManager::<TestStruct, 3>::new();
794    }
795}