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;
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)]
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
72/// This structure maintains the entire memory needed by Copper for one loop for the inter tasks communication within a process.
73/// P or Payload is typically a Tuple of various types of messages that are exchanged between tasks.
74/// N is the maximum number of in flight Copper List the runtime can support.
75pub struct CuListsManager<P: CopperListTuple, const N: usize> {
76    data: Box<[CopperList<P>; N]>,
77    length: usize,
78    insertion_index: usize,
79    current_cl_id: u32,
80}
81
82impl<P: CopperListTuple + fmt::Debug, const N: usize> fmt::Debug for CuListsManager<P, N> {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.debug_struct("CuListsManager")
85            .field("data", &self.data)
86            .field("length", &self.length)
87            .field("insertion_index", &self.insertion_index)
88            // Do not include on_drop field
89            .finish()
90    }
91}
92
93pub type Iter<'a, T> = Chain<Rev<SliceIter<'a, T>>, Rev<SliceIter<'a, T>>>;
94pub type IterMut<'a, T> = Chain<Rev<SliceIterMut<'a, T>>, Rev<SliceIterMut<'a, T>>>;
95pub type AscIter<'a, T> = Chain<SliceIter<'a, T>, SliceIter<'a, T>>;
96pub type AscIterMut<'a, T> = Chain<SliceIterMut<'a, T>, SliceIterMut<'a, T>>;
97
98impl<P: CopperListTuple, const N: usize> Default for CuListsManager<P, N> {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl<P: CopperListTuple, const N: usize> CuListsManager<P, N> {
105    pub fn new() -> Self {
106        let data = unsafe {
107            let layout = std::alloc::Layout::new::<[CopperList<P>; N]>();
108            let ptr = std::alloc::alloc_zeroed(layout) as *mut [CopperList<P>; N];
109            Box::from_raw(ptr)
110        };
111        CuListsManager {
112            data,
113            length: 0,
114            insertion_index: 0,
115            current_cl_id: 0,
116        }
117    }
118
119    /// Returns the current number of elements in the queue.
120    ///
121    #[inline]
122    pub fn len(&self) -> usize {
123        self.length
124    }
125
126    /// Returns `true` if the queue contains no elements.
127    ///
128    #[inline]
129    pub fn is_empty(&self) -> bool {
130        self.length == 0
131    }
132
133    /// Returns `true` if the queue is full.
134    ///
135    #[inline]
136    pub fn is_full(&self) -> bool {
137        N == self.len()
138    }
139
140    /// Clears the queue.
141    ///
142    #[inline]
143    pub fn clear(&mut self) {
144        self.insertion_index = 0;
145        self.length = 0;
146    }
147
148    #[inline]
149    pub fn create(&mut self) -> Option<&mut CopperList<P>> {
150        if self.is_full() {
151            return None;
152        }
153        let result = &mut self.data[self.insertion_index];
154        self.insertion_index = (self.insertion_index + 1) % N;
155        self.length += 1;
156
157        // We assign a unique id to each CopperList to be able to track them across their lifetime.
158        result.id = self.current_cl_id;
159        self.current_cl_id += 1;
160
161        Some(result)
162    }
163
164    /// Peeks at the last element in the queue.
165    #[inline]
166    pub fn peek(&self) -> Option<&CopperList<P>> {
167        if self.length == 0 {
168            return None;
169        }
170        let index = if self.insertion_index == 0 {
171            N - 1
172        } else {
173            self.insertion_index - 1
174        };
175        Some(&self.data[index])
176    }
177
178    #[inline]
179    #[allow(dead_code)]
180    fn drop_last(&mut self) {
181        if self.length == 0 {
182            return;
183        }
184        if self.insertion_index == 0 {
185            self.insertion_index = N - 1;
186        } else {
187            self.insertion_index -= 1;
188        }
189        self.length -= 1;
190    }
191
192    #[inline]
193    pub fn pop(&mut self) -> Option<&mut CopperList<P>> {
194        if self.length == 0 {
195            return None;
196        }
197        if self.insertion_index == 0 {
198            self.insertion_index = N - 1;
199        } else {
200            self.insertion_index -= 1;
201        }
202        self.length -= 1;
203        Some(&mut self.data[self.insertion_index])
204    }
205
206    /// Returns an iterator over the queue's contents.
207    ///
208    /// The iterator goes from the most recently pushed items to the oldest ones.
209    ///
210    #[inline]
211    pub fn iter(&self) -> Iter<CopperList<P>> {
212        let (a, b) = self.data[0..self.length].split_at(self.insertion_index);
213        a.iter().rev().chain(b.iter().rev())
214    }
215
216    /// Returns a mutable iterator over the queue's contents.
217    ///
218    /// The iterator goes from the most recently pushed items to the oldest ones.
219    ///
220    #[inline]
221    pub fn iter_mut(&mut self) -> IterMut<CopperList<P>> {
222        let (a, b) = self.data.split_at_mut(self.insertion_index);
223        a.iter_mut().rev().chain(b.iter_mut().rev())
224    }
225
226    /// Returns an ascending iterator over the queue's contents.
227    ///
228    /// The iterator goes from the least recently pushed items to the newest ones.
229    ///
230    #[inline]
231    pub fn asc_iter(&self) -> AscIter<CopperList<P>> {
232        let (a, b) = self.data.split_at(self.insertion_index);
233        b.iter().chain(a.iter())
234    }
235
236    /// Returns a mutable ascending iterator over the queue's contents.
237    ///
238    /// The iterator goes from the least recently pushed items to the newest ones.
239    ///
240    #[inline]
241    pub fn asc_iter_mut(&mut self) -> AscIterMut<CopperList<P>> {
242        let (a, b) = self.data.split_at_mut(self.insertion_index);
243        b.iter_mut().chain(a.iter_mut())
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn empty_queue() {
253        let q = CuListsManager::<i32, 5>::new();
254
255        assert!(q.is_empty());
256        assert!(q.iter().next().is_none());
257    }
258
259    #[test]
260    fn partially_full_queue() {
261        let mut q = CuListsManager::<i32, 5>::new();
262        q.create().unwrap().msgs = 1;
263        q.create().unwrap().msgs = 2;
264        q.create().unwrap().msgs = 3;
265
266        assert!(!q.is_empty());
267        assert_eq!(q.len(), 3);
268
269        let res: Vec<i32> = q.iter().map(|x| x.msgs).collect();
270        assert_eq!(res, [3, 2, 1]);
271    }
272
273    #[test]
274    fn full_queue() {
275        let mut q = CuListsManager::<i32, 5>::new();
276        q.create().unwrap().msgs = 1;
277        q.create().unwrap().msgs = 2;
278        q.create().unwrap().msgs = 3;
279        q.create().unwrap().msgs = 4;
280        q.create().unwrap().msgs = 5;
281        assert_eq!(q.len(), 5);
282
283        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
284        assert_eq!(res, [5, 4, 3, 2, 1]);
285    }
286
287    #[test]
288    fn over_full_queue() {
289        let mut q = CuListsManager::<i32, 5>::new();
290        q.create().unwrap().msgs = 1;
291        q.create().unwrap().msgs = 2;
292        q.create().unwrap().msgs = 3;
293        q.create().unwrap().msgs = 4;
294        q.create().unwrap().msgs = 5;
295        assert!(q.create().is_none());
296        assert_eq!(q.len(), 5);
297
298        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
299        assert_eq!(res, [5, 4, 3, 2, 1]);
300    }
301
302    #[test]
303    fn clear() {
304        let mut q = CuListsManager::<i32, 5>::new();
305        q.create().unwrap().msgs = 1;
306        q.create().unwrap().msgs = 2;
307        q.create().unwrap().msgs = 3;
308        q.create().unwrap().msgs = 4;
309        q.create().unwrap().msgs = 5;
310        assert!(q.create().is_none());
311        assert_eq!(q.len(), 5);
312
313        q.clear();
314
315        assert_eq!(q.len(), 0);
316        assert!(q.iter().next().is_none());
317
318        q.create().unwrap().msgs = 1;
319        q.create().unwrap().msgs = 2;
320        q.create().unwrap().msgs = 3;
321
322        assert_eq!(q.len(), 3);
323
324        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
325        assert_eq!(res, [3, 2, 1]);
326    }
327
328    #[test]
329    fn mutable_iterator() {
330        let mut q = CuListsManager::<i32, 5>::new();
331        q.create().unwrap().msgs = 1;
332        q.create().unwrap().msgs = 2;
333        q.create().unwrap().msgs = 3;
334        q.create().unwrap().msgs = 4;
335        q.create().unwrap().msgs = 5;
336
337        for x in q.iter_mut() {
338            x.msgs *= 2;
339        }
340
341        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
342        assert_eq!(res, [10, 8, 6, 4, 2]);
343    }
344
345    #[test]
346    fn zero_sized() {
347        let mut q = CuListsManager::<(), 5>::new();
348        *q.create().unwrap() = CopperList::new(0, ());
349        *q.create().unwrap() = CopperList::new(1, ());
350        *q.create().unwrap() = CopperList::new(2, ());
351
352        assert_eq!(q.len(), 3);
353
354        let mut iter = q.iter();
355        iter.next().unwrap();
356        iter.next().unwrap();
357        iter.next().unwrap();
358        assert!(iter.next().is_none());
359    }
360
361    #[test]
362    fn be_sure_we_wont_stackoverflow_at_init() {
363        let _ = CuListsManager::<[u8; 10_000_000], 3>::new();
364    }
365
366    #[test]
367    fn test_drop_last() {
368        let mut q = CuListsManager::<i32, 5>::new();
369        q.create().unwrap().msgs = 1;
370        q.create().unwrap().msgs = 2;
371        q.create().unwrap().msgs = 3;
372        q.create().unwrap().msgs = 4;
373        q.create().unwrap().msgs = 5;
374        assert_eq!(q.len(), 5);
375
376        q.drop_last();
377        assert_eq!(q.len(), 4);
378
379        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
380        assert_eq!(res, [4, 3, 2, 1]);
381    }
382
383    #[test]
384    fn test_pop() {
385        let mut q = CuListsManager::<i32, 5>::new();
386        q.create().unwrap().msgs = 1;
387        q.create().unwrap().msgs = 2;
388        q.create().unwrap().msgs = 3;
389        q.create().unwrap().msgs = 4;
390        q.create().unwrap().msgs = 5;
391        assert_eq!(q.len(), 5);
392
393        let last = q.pop().unwrap();
394        assert_eq!(last.msgs, 5);
395        assert_eq!(q.len(), 4);
396
397        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
398        assert_eq!(res, [4, 3, 2, 1]);
399    }
400
401    #[test]
402    fn test_peek() {
403        let mut q = CuListsManager::<i32, 5>::new();
404        q.create().unwrap().msgs = 1;
405        q.create().unwrap().msgs = 2;
406        q.create().unwrap().msgs = 3;
407        q.create().unwrap().msgs = 4;
408        q.create().unwrap().msgs = 5;
409        assert_eq!(q.len(), 5);
410
411        let last = q.peek().unwrap();
412        assert_eq!(last.msgs, 5);
413        assert_eq!(q.len(), 5);
414
415        let res: Vec<_> = q.iter().map(|x| x.msgs).collect();
416        assert_eq!(res, [5, 4, 3, 2, 1]);
417    }
418}