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::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, PartialEq, Copy, Clone)]
32pub enum CopperListState {
33    Free,
34    Initialized,
35    Processing,
36    DoneProcessing,
37    BeingSerialized,
38}
39
40impl Display for CopperListState {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            CopperListState::Free => write!(f, "Free"),
44            CopperListState::Initialized => write!(f, "Initialized"),
45            CopperListState::Processing => write!(f, "Processing"),
46            CopperListState::DoneProcessing => write!(f, "DoneProcessing"),
47            CopperListState::BeingSerialized => write!(f, "BeingSerialized"),
48        }
49    }
50}
51
52#[derive(Debug, Encode, Decode, Serialize)]
53pub struct CopperList<P: CopperListTuple> {
54    pub id: u32,
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: u32, 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
88impl<P: CopperListTuple> ErasedCuStampedDataSet for CopperList<P> {
89    fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
90        self.msgs.cumsgs()
91    }
92}
93
94/// This structure maintains the entire memory needed by Copper for one loop for the inter tasks communication within a process.
95/// P or Payload is typically a Tuple of various types of messages that are exchanged between tasks.
96/// N is the maximum number of in flight Copper List the runtime can support.
97pub struct CuListsManager<P: CopperListTuple, const N: usize> {
98    data: Box<[CopperList<P>; N]>,
99    length: usize,
100    insertion_index: usize,
101    current_cl_id: u32,
102}
103
104impl<P: CopperListTuple + fmt::Debug, const N: usize> fmt::Debug for CuListsManager<P, N> {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        f.debug_struct("CuListsManager")
107            .field("data", &self.data)
108            .field("length", &self.length)
109            .field("insertion_index", &self.insertion_index)
110            // Do not include on_drop field
111            .finish()
112    }
113}
114
115pub type Iter<'a, T> = Chain<Rev<SliceIter<'a, T>>, Rev<SliceIter<'a, T>>>;
116pub type IterMut<'a, T> = Chain<Rev<SliceIterMut<'a, T>>, Rev<SliceIterMut<'a, T>>>;
117pub type AscIter<'a, T> = Chain<SliceIter<'a, T>, SliceIter<'a, T>>;
118pub type AscIterMut<'a, T> = Chain<SliceIterMut<'a, T>, SliceIterMut<'a, T>>;
119
120/// Initializes fields that cannot be zeroed after allocating a zeroed
121/// [`CopperList`].
122pub trait CuListZeroedInit: CopperListTuple {
123    /// Fixes up a zero-initialized copper list so that all internal fields are
124    /// in a valid state.
125    fn init_zeroed(&mut self);
126}
127
128impl<P: CopperListTuple + CuListZeroedInit, const N: usize> Default for CuListsManager<P, N> {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134impl<P: CopperListTuple, const N: usize> CuListsManager<P, N> {
135    pub fn new() -> Self
136    where
137        P: CuListZeroedInit,
138    {
139        let data = unsafe {
140            let layout = Layout::new::<[CopperList<P>; N]>();
141            let ptr = alloc_zeroed(layout) as *mut [CopperList<P>; N];
142            if ptr.is_null() {
143                handle_alloc_error(layout);
144            }
145            Box::from_raw(ptr)
146        };
147        let mut manager = CuListsManager {
148            data,
149            length: 0,
150            insertion_index: 0,
151            current_cl_id: 0,
152        };
153
154        for cl in manager.data.iter_mut() {
155            cl.msgs.init_zeroed();
156        }
157
158        manager
159    }
160
161    /// Returns the current number of elements in the queue.
162    ///
163    #[inline]
164    pub fn len(&self) -> usize {
165        self.length
166    }
167
168    /// Returns `true` if the queue contains no elements.
169    ///
170    #[inline]
171    pub fn is_empty(&self) -> bool {
172        self.length == 0
173    }
174
175    /// Returns `true` if the queue is full.
176    ///
177    #[inline]
178    pub fn is_full(&self) -> bool {
179        N == self.len()
180    }
181
182    /// Clears the queue.
183    ///
184    #[inline]
185    pub fn clear(&mut self) {
186        self.insertion_index = 0;
187        self.length = 0;
188    }
189
190    #[inline]
191    pub fn create(&mut self) -> Option<&mut CopperList<P>> {
192        if self.is_full() {
193            return None;
194        }
195        let result = &mut self.data[self.insertion_index];
196        self.insertion_index = (self.insertion_index + 1) % N;
197        self.length += 1;
198
199        // We assign a unique id to each CopperList to be able to track them across their lifetime.
200        result.id = self.current_cl_id;
201        self.current_cl_id += 1;
202
203        Some(result)
204    }
205
206    /// Peeks at the last element in the queue.
207    #[inline]
208    pub fn peek(&self) -> Option<&CopperList<P>> {
209        if self.length == 0 {
210            return None;
211        }
212        let index = if self.insertion_index == 0 {
213            N - 1
214        } else {
215            self.insertion_index - 1
216        };
217        Some(&self.data[index])
218    }
219
220    #[inline]
221    #[allow(dead_code)]
222    fn drop_last(&mut self) {
223        if self.length == 0 {
224            return;
225        }
226        if self.insertion_index == 0 {
227            self.insertion_index = N - 1;
228        } else {
229            self.insertion_index -= 1;
230        }
231        self.length -= 1;
232    }
233
234    #[inline]
235    pub fn pop(&mut self) -> Option<&mut CopperList<P>> {
236        if self.length == 0 {
237            return None;
238        }
239        if self.insertion_index == 0 {
240            self.insertion_index = N - 1;
241        } else {
242            self.insertion_index -= 1;
243        }
244        self.length -= 1;
245        Some(&mut self.data[self.insertion_index])
246    }
247
248    /// Returns an iterator over the queue's contents.
249    ///
250    /// The iterator goes from the most recently pushed items to the oldest ones.
251    ///
252    #[inline]
253    pub fn iter(&self) -> Iter<'_, CopperList<P>> {
254        let (a, b) = self.data[0..self.length].split_at(self.insertion_index);
255        a.iter().rev().chain(b.iter().rev())
256    }
257
258    /// Returns a mutable iterator over the queue's contents.
259    ///
260    /// The iterator goes from the most recently pushed items to the oldest ones.
261    ///
262    #[inline]
263    pub fn iter_mut(&mut self) -> IterMut<'_, CopperList<P>> {
264        let (a, b) = self.data.split_at_mut(self.insertion_index);
265        a.iter_mut().rev().chain(b.iter_mut().rev())
266    }
267
268    /// Returns an ascending iterator over the queue's contents.
269    ///
270    /// The iterator goes from the least recently pushed items to the newest ones.
271    ///
272    #[inline]
273    pub fn asc_iter(&self) -> AscIter<'_, CopperList<P>> {
274        let (a, b) = self.data.split_at(self.insertion_index);
275        b.iter().chain(a.iter())
276    }
277
278    /// Returns a mutable ascending iterator over the queue's contents.
279    ///
280    /// The iterator goes from the least recently pushed items to the newest ones.
281    ///
282    #[inline]
283    pub fn asc_iter_mut(&mut self) -> AscIterMut<'_, CopperList<P>> {
284        let (a, b) = self.data.split_at_mut(self.insertion_index);
285        b.iter_mut().chain(a.iter_mut())
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292    use cu29_traits::{ErasedCuStampedData, ErasedCuStampedDataSet, MatchingTasks};
293    use serde::{Serialize, Serializer};
294
295    #[derive(Debug, Encode, Decode, PartialEq, Clone, Copy, Serialize, Default)]
296    struct CuStampedDataSet(i32);
297
298    impl ErasedCuStampedDataSet for CuStampedDataSet {
299        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
300            Vec::new()
301        }
302    }
303
304    impl MatchingTasks for CuStampedDataSet {
305        fn get_all_task_ids() -> &'static [&'static str] {
306            &[]
307        }
308    }
309
310    impl CuListZeroedInit for CuStampedDataSet {
311        fn init_zeroed(&mut self) {}
312    }
313
314    #[test]
315    fn empty_queue() {
316        let q = CuListsManager::<CuStampedDataSet, 5>::new();
317
318        assert!(q.is_empty());
319        assert!(q.iter().next().is_none());
320    }
321
322    #[test]
323    fn partially_full_queue() {
324        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
325        q.create().unwrap().msgs.0 = 1;
326        q.create().unwrap().msgs.0 = 2;
327        q.create().unwrap().msgs.0 = 3;
328
329        assert!(!q.is_empty());
330        assert_eq!(q.len(), 3);
331
332        let res: Vec<i32> = q.iter().map(|x| x.msgs.0).collect();
333        assert_eq!(res, [3, 2, 1]);
334    }
335
336    #[test]
337    fn full_queue() {
338        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
339        q.create().unwrap().msgs.0 = 1;
340        q.create().unwrap().msgs.0 = 2;
341        q.create().unwrap().msgs.0 = 3;
342        q.create().unwrap().msgs.0 = 4;
343        q.create().unwrap().msgs.0 = 5;
344        assert_eq!(q.len(), 5);
345
346        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
347        assert_eq!(res, [5, 4, 3, 2, 1]);
348    }
349
350    #[test]
351    fn over_full_queue() {
352        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
353        q.create().unwrap().msgs.0 = 1;
354        q.create().unwrap().msgs.0 = 2;
355        q.create().unwrap().msgs.0 = 3;
356        q.create().unwrap().msgs.0 = 4;
357        q.create().unwrap().msgs.0 = 5;
358        assert!(q.create().is_none());
359        assert_eq!(q.len(), 5);
360
361        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
362        assert_eq!(res, [5, 4, 3, 2, 1]);
363    }
364
365    #[test]
366    fn clear() {
367        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
368        q.create().unwrap().msgs.0 = 1;
369        q.create().unwrap().msgs.0 = 2;
370        q.create().unwrap().msgs.0 = 3;
371        q.create().unwrap().msgs.0 = 4;
372        q.create().unwrap().msgs.0 = 5;
373        assert!(q.create().is_none());
374        assert_eq!(q.len(), 5);
375
376        q.clear();
377
378        assert_eq!(q.len(), 0);
379        assert!(q.iter().next().is_none());
380
381        q.create().unwrap().msgs.0 = 1;
382        q.create().unwrap().msgs.0 = 2;
383        q.create().unwrap().msgs.0 = 3;
384
385        assert_eq!(q.len(), 3);
386
387        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
388        assert_eq!(res, [3, 2, 1]);
389    }
390
391    #[test]
392    fn mutable_iterator() {
393        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
394        q.create().unwrap().msgs.0 = 1;
395        q.create().unwrap().msgs.0 = 2;
396        q.create().unwrap().msgs.0 = 3;
397        q.create().unwrap().msgs.0 = 4;
398        q.create().unwrap().msgs.0 = 5;
399
400        for x in q.iter_mut() {
401            x.msgs.0 *= 2;
402        }
403
404        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
405        assert_eq!(res, [10, 8, 6, 4, 2]);
406    }
407
408    #[test]
409    fn test_drop_last() {
410        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
411        q.create().unwrap().msgs.0 = 1;
412        q.create().unwrap().msgs.0 = 2;
413        q.create().unwrap().msgs.0 = 3;
414        q.create().unwrap().msgs.0 = 4;
415        q.create().unwrap().msgs.0 = 5;
416        assert_eq!(q.len(), 5);
417
418        q.drop_last();
419        assert_eq!(q.len(), 4);
420
421        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
422        assert_eq!(res, [4, 3, 2, 1]);
423    }
424
425    #[test]
426    fn test_pop() {
427        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
428        q.create().unwrap().msgs.0 = 1;
429        q.create().unwrap().msgs.0 = 2;
430        q.create().unwrap().msgs.0 = 3;
431        q.create().unwrap().msgs.0 = 4;
432        q.create().unwrap().msgs.0 = 5;
433        assert_eq!(q.len(), 5);
434
435        let last = q.pop().unwrap();
436        assert_eq!(last.msgs.0, 5);
437        assert_eq!(q.len(), 4);
438
439        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
440        assert_eq!(res, [4, 3, 2, 1]);
441    }
442
443    #[test]
444    fn test_peek() {
445        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
446        q.create().unwrap().msgs.0 = 1;
447        q.create().unwrap().msgs.0 = 2;
448        q.create().unwrap().msgs.0 = 3;
449        q.create().unwrap().msgs.0 = 4;
450        q.create().unwrap().msgs.0 = 5;
451        assert_eq!(q.len(), 5);
452
453        let last = q.peek().unwrap();
454        assert_eq!(last.msgs.0, 5);
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    #[derive(Decode, Encode, Debug, PartialEq, Clone, Copy)]
462    struct TestStruct {
463        content: [u8; 10_000_000],
464    }
465
466    impl Default for TestStruct {
467        fn default() -> Self {
468            TestStruct {
469                content: [0; 10_000_000],
470            }
471        }
472    }
473
474    impl ErasedCuStampedDataSet for TestStruct {
475        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
476            Vec::new()
477        }
478    }
479
480    impl Serialize for TestStruct {
481        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
482        where
483            S: Serializer,
484        {
485            serializer.serialize_i8(0)
486        }
487    }
488
489    impl MatchingTasks for TestStruct {
490        fn get_all_task_ids() -> &'static [&'static str] {
491            &[]
492        }
493    }
494
495    impl CuListZeroedInit for TestStruct {
496        fn init_zeroed(&mut self) {}
497    }
498
499    #[test]
500    fn be_sure_we_wont_stackoverflow_at_init() {
501        let _ = CuListsManager::<TestStruct, 3>::new();
502    }
503}