From 025a80fc285a01199c48a9b5206d7190db5c484d Mon Sep 17 00:00:00 2001 From: Guillaume Binet Date: Wed, 22 May 2024 12:53:18 -0400 Subject: [PATCH] might have fixed this circular buffer misery --- copper/src/common.rs | 167 ++++++++++++++------------------------- copper_derive/src/lib.rs | 5 +- 2 files changed, 61 insertions(+), 111 deletions(-) diff --git a/copper/src/common.rs b/copper/src/common.rs index 5f4160585..66788ef05 100644 --- a/copper/src/common.rs +++ b/copper/src/common.rs @@ -1,29 +1,29 @@ -//! A circular buffer-like queue. -//! (Adapted by gbin for fixed size and inplace) -//! -//! The `CircularQueue` is created with a set capacity, then items are pushed in. When the queue -//! runs out of capacity, newer items start overwriting the old ones, starting from the oldest. -//! -//! There are built-in iterators that go from the newest items to the oldest ones and from the -//! oldest items to the newest ones. -//! -//! Two queues are considered equal if iterating over them with `iter()` would yield the same -//! sequence of elements. -//! -//! - extern crate alloc; use std::iter::{Chain, Rev}; -use std::mem::{replace, MaybeUninit}; +use std::mem::replace; use std::slice::{Iter as SliceIter, IterMut as SliceIterMut}; -pub use generic_array::typenum::consts::*; +const MAX_TASKS: usize = 255; +const MAX_CONCURRENT_TASKS: usize = 8; + +#[derive(Debug)] +struct CopperLiskMask { + mask: [u128; MAX_TASKS / 128 + 1], +} + +#[derive(Debug)] +enum CopperListState { + Empty, + PrecessingTasks(CopperLiskMask), + BeingSerialized, +} /// A circular buffer-like queue. #[derive(Debug)] -pub struct CircularQueue { - data: [MaybeUninit; N], +pub struct CopperListsManager { + // copper_list_masks: CopperListMask, + data: Box<[T; N]>, length: usize, insertion_index: usize, } @@ -43,26 +43,24 @@ pub type AscIterMut<'a, T> = Chain, SliceIterMut<'a, T>>; /// A value popped from `CircularQueue` as the result of a push operation. pub type Popped = Option; -impl PartialEq for CircularQueue { +impl PartialEq for CopperListsManager { fn eq(&self, other: &Self) -> bool { if self.len() != other.len() { return false; } other.iter().zip(self.iter()).all(|(a, b)| a == b) } - - fn ne(&self, other: &Self) -> bool { - if self.len() != other.len() { - return true; - } - !self.eq(other) - } } -impl CircularQueue { +impl CopperListsManager { pub fn new() -> Self { - CircularQueue { - data: unsafe { MaybeUninit::uninit().assume_init() }, + let data = unsafe { + let layout = std::alloc::Layout::new::<[T; N]>(); + let ptr = std::alloc::alloc_zeroed(layout) as *mut [T; N]; + Box::from_raw(ptr) + }; + CopperListsManager { + data, length: 0, insertion_index: 0, } @@ -104,14 +102,6 @@ impl CircularQueue self.length = 0; } - unsafe fn initialized_slice(&self) -> &[T] { - &*(self.data.get_unchecked(0..self.length) as *const _ as *const [T]) - } - - unsafe fn initialized_slice_mut(&mut self) -> &mut [T] { - &mut *(self.data.get_unchecked_mut(0..self.length) as *mut _ as *mut [T]) - } - /// Pushes a new element into the queue. /// /// Once the capacity is reached, pushing new items will overwrite old ones. @@ -126,39 +116,30 @@ impl CircularQueue return old; } - if self.is_full() { - // Replace the element and take ownership of the old value - unsafe { - old = Some(self.data[self.insertion_index].as_ptr().read()); - } - } else { + if !self.is_full() { + self.data[self.insertion_index] = x; self.length += 1; + } else { + old = Some(replace(&mut self.data[self.insertion_index], x)); } - // Write the new element in place - unsafe { - self.data[self.insertion_index].as_mut_ptr().write(x); - } self.insertion_index = (self.insertion_index + 1) % self.capacity(); old } #[inline] - pub fn pop(&mut self) -> Option { + pub fn pop(&mut self) -> Popped<&T> { if self.capacity() == 0 || self.length == 0 { return None; } - if self.insertion_index == 0 { self.insertion_index = self.capacity() - 1; } else { self.insertion_index -= 1; } self.length -= 1; - - // Take ownership of the element - unsafe { Some(self.data[self.insertion_index].as_ptr().read()) } + Some(&self.data[self.insertion_index]) } /// Returns an iterator over the queue's contents. @@ -167,8 +148,7 @@ impl CircularQueue /// #[inline] pub fn iter(&self) -> Iter { - let initialized_part = unsafe { self.initialized_slice() }; - let (a, b) = initialized_part.split_at(self.insertion_index); + let (a, b) = self.data[0..self.length].split_at(self.insertion_index); a.iter().rev().chain(b.iter().rev()) } @@ -178,9 +158,7 @@ impl CircularQueue /// #[inline] pub fn iter_mut(&mut self) -> IterMut { - let insertion_index = self.insertion_index; - let initialized_part = unsafe { self.initialized_slice_mut() }; - let (a, b) = initialized_part.split_at_mut(insertion_index); + let (a, b) = self.data.split_at_mut(self.insertion_index); a.iter_mut().rev().chain(b.iter_mut().rev()) } @@ -190,8 +168,7 @@ impl CircularQueue /// #[inline] pub fn asc_iter(&self) -> AscIter { - let initialized_part = unsafe { self.initialized_slice() }; - let (a, b) = initialized_part.split_at(self.insertion_index); + let (a, b) = self.data.split_at(self.insertion_index); b.iter().chain(a.iter()) } @@ -201,9 +178,7 @@ impl CircularQueue /// #[inline] pub fn asc_iter_mut(&mut self) -> AscIterMut { - let insertion_index = self.insertion_index; - let initialized_part = unsafe { self.initialized_slice_mut() }; - let (a, b) = initialized_part.split_at_mut(insertion_index); + let (a, b) = self.data.split_at_mut(self.insertion_index); b.iter_mut().chain(a.iter_mut()) } } @@ -214,7 +189,7 @@ mod tests { #[test] fn zero_capacity() { - let mut q = CircularQueue::::new(); + let mut q = CopperListsManager::::new(); assert_eq!(q.len(), 0); assert_eq!(q.capacity(), 0); assert!(q.is_empty()); @@ -235,7 +210,7 @@ mod tests { #[test] fn empty_queue() { - let q = CircularQueue::::new(); + let q = CopperListsManager::::new(); assert!(q.is_empty()); assert_eq!(q.iter().next(), None); @@ -243,7 +218,7 @@ mod tests { #[test] fn partially_full_queue() { - let mut q = CircularQueue::<_, 5>::new(); + let mut q = CopperListsManager::<_, 5>::new(); q.push(1); q.push(2); q.push(3); @@ -257,7 +232,7 @@ mod tests { #[test] fn full_queue() { - let mut q = CircularQueue::<_, 5>::new(); + let mut q = CopperListsManager::<_, 5>::new(); q.push(1); q.push(2); q.push(3); @@ -272,7 +247,7 @@ mod tests { #[test] fn over_full_queue() { - let mut q = CircularQueue::<_, 5>::new(); + let mut q = CopperListsManager::<_, 5>::new(); q.push(1); q.push(2); q.push(3); @@ -289,7 +264,7 @@ mod tests { #[test] fn clear() { - let mut q = CircularQueue::<_, 5>::new(); + let mut q = CopperListsManager::<_, 5>::new(); q.push(1); q.push(2); q.push(3); @@ -315,7 +290,7 @@ mod tests { #[test] fn mutable_iterator() { - let mut q = CircularQueue::<_, 5>::new(); + let mut q = CopperListsManager::<_, 5>::new(); q.push(1); q.push(2); q.push(3); @@ -334,7 +309,7 @@ mod tests { #[test] fn zero_sized() { - let mut q = CircularQueue::<_, 5>::new(); + let mut q = CopperListsManager::<_, 5>::new(); assert_eq!(q.capacity(), 5); q.push(()); @@ -352,8 +327,8 @@ mod tests { #[test] fn empty_queue_eq() { - let q1 = CircularQueue::::new(); - let q2 = CircularQueue::::new(); + let q1 = CopperListsManager::::new(); + let q2 = CopperListsManager::::new(); assert_eq!(q1, q2); // I am not sure here @@ -363,12 +338,12 @@ mod tests { #[test] fn partially_full_queue_eq() { - let mut q1 = CircularQueue::::new(); + let mut q1 = CopperListsManager::::new(); q1.push(1); q1.push(2); q1.push(3); - let mut q2 = CircularQueue::::new(); + let mut q2 = CopperListsManager::::new(); q2.push(1); q2.push(2); assert_ne!(q1, q2); @@ -382,14 +357,14 @@ mod tests { #[test] fn full_queue_eq() { - let mut q1 = CircularQueue::::new(); + let mut q1 = CopperListsManager::::new(); q1.push(1); q1.push(2); q1.push(3); q1.push(4); q1.push(5); - let mut q2 = CircularQueue::::new(); + let mut q2 = CopperListsManager::::new(); q2.push(1); q2.push(2); q2.push(3); @@ -401,7 +376,7 @@ mod tests { #[test] fn over_full_queue_eq() { - let mut q1 = CircularQueue::::new(); + let mut q1 = CopperListsManager::::new(); q1.push(1); q1.push(2); q1.push(3); @@ -410,7 +385,7 @@ mod tests { q1.push(6); q1.push(7); - let mut q2 = CircularQueue::::new(); + let mut q2 = CopperListsManager::::new(); q2.push(1); q2.push(2); q2.push(3); @@ -435,7 +410,7 @@ mod tests { #[test] fn clear_eq() { - let mut q1 = CircularQueue::::new(); + let mut q1 = CopperListsManager::::new(); q1.push(1); q1.push(2); q1.push(3); @@ -445,7 +420,7 @@ mod tests { q1.push(7); q1.clear(); - let mut q2 = CircularQueue::::new(); + let mut q2 = CopperListsManager::::new(); assert_eq!(q1, q2); q2.push(1); @@ -455,13 +430,13 @@ mod tests { #[test] fn zero_sized_eq() { - let mut q1 = CircularQueue::<_, 3>::new(); + let mut q1 = CopperListsManager::<_, 3>::new(); q1.push(()); q1.push(()); q1.push(()); q1.push(()); - let mut q2 = CircularQueue::<_, 3>::new(); + let mut q2 = CopperListsManager::<_, 3>::new(); q2.push(()); q2.push(()); assert_ne!(q1, q2); @@ -476,32 +451,8 @@ mod tests { assert_eq!(q1, q2); } - struct Big { - _data: [u8; 10000000], - } - impl Default for Big { - fn default() -> Self { - Big { - _data: [0; 10000000], - } - } - } - - impl Clone for Big { - fn clone(&self) -> Self { - Big { _data: self._data } - } - } - - impl Copy for Big {} - impl PartialEq for Big { - fn eq(&self, _: &Self) -> bool { - true - } - } - #[test] - fn no_stack_allocation() { - let _ = CircularQueue::::new(); // this should not stack overflow + fn be_sure_we_wont_stackoverflow_at_init() { + let _ = CopperListsManager::<[u8; 10_000_000], 3>::new(); } } diff --git a/copper_derive/src/lib.rs b/copper_derive/src/lib.rs index 7a4d72eff..8c7370c46 100644 --- a/copper_derive/src/lib.rs +++ b/copper_derive/src/lib.rs @@ -60,7 +60,7 @@ pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream { let msgs_types_tuple: TypeTuple = parse_quote! { (#(#all_msgs_types),*,)}; let copper_lists_field: Field = parse_quote! { - copper_lists: CircularQueue + copper_lists: CircularQueue }; let name = &item_struct.ident; @@ -110,7 +110,6 @@ pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream { let result = quote! { use std::fs::read_to_string; use copper::common::CircularQueue; - use copper::common::U1; use copper::config::Node; use copper::config::CopperConfig; use copper::config::NodeInstanceConfig; @@ -139,7 +138,7 @@ pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream { ); Ok(#name { task_instances, - copper_lists: Box::new(CircularQueue::new()), + copper_lists: CircularQueue::new(), }) }