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