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