diff --git a/Cargo.toml b/Cargo.toml index 1396ba43be3a8027b9f60b10cc55eae015f66645..5e023e014e7182ed682387b700973a071831ec3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cortex-m = "0.7.2" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..9b7a0398e436a805d8b4b79082ff7aeef8a62fce --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,155 @@ +#![no_std] +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut, Drop}; +use cortex_m::interrupt; + +/// Internal replacement for `static mut T` +// TODO: Decide name and location. +pub struct RacyCell<T>(UnsafeCell<T>); + +impl<T> RacyCell<T> { + /// Create a RacyCell + pub const fn new(value: T) -> Self { + RacyCell(UnsafeCell::new(value)) + } + + /// Get &mut T + pub unsafe fn get_mut_unchecked(&self) -> &mut T { + &mut *self.0.get() + } +} + +// The type wrapped need to be Sync for RacyCell<T> to be Sync +//unsafe impl<T> Sync for RacyCell<T> where T: Sync {} +unsafe impl<T> Sync for RacyCell<T> {} + +#[inline] +pub fn interrupt_free<F, R>(f: F) -> R +where + F: FnOnce() -> R, +{ + if cfg!(target_arch = "arm") { + interrupt::free(|_| f()) + } else { + f() + } +} + +#[derive(Debug)] +pub enum Error { + Uninitialized, + OutOfMemory, + AlreadyInitialized, +} + +pub struct Box<T: 'static + Sized, const S: usize> { + index: usize, + allocator: &'static PoolMan<T, S>, +} + +impl<T: 'static, const S: usize> Drop for Box<T, S> { + fn drop(&mut self) { + self.allocator.dealloc(self.index) + } +} + +impl<T: 'static, const S: usize> Deref for Box<T, S> { + type Target = T; + fn deref(&self) -> &T { + unsafe { + self.allocator + .data /* RacyCell */ + .get_mut_unchecked() /* Array */ + .get_unchecked(self.index) + } + } +} + +impl<T: 'static, const S: usize> DerefMut for Box<T, S> { + fn deref_mut(&mut self) -> &mut T { + unsafe { + self.allocator + .data /* RacyCell */ + .get_mut_unchecked() /* Array */ + .get_unchecked_mut(self.index) + } + } +} + +pub struct PoolMan<T: 'static, const S: usize> { + data: RacyCell<[T; S]>, + free: RacyCell<[usize; S]>, + head: RacyCell<usize>, + init: RacyCell<bool>, +} + +impl<T, const S: usize> PoolMan<T, S> +where + T: Sized, +{ + pub const fn new(v: [T; S]) -> Self + where + T: Sized, + { + Self { + data: RacyCell::new(v), + free: RacyCell::new([0; S]), + head: RacyCell::new(0), + init: RacyCell::new(false), + } + } + + #[inline] + pub fn init(&self) -> Result<(), Error> { + interrupt_free(|| { + if *unsafe { self.init.get_mut_unchecked() } { + Err(Error::AlreadyInitialized) + } else { + let free = unsafe { self.free.get_mut_unchecked() }; + for (index, value) in free.iter_mut().enumerate() { + *value = index + 1; + } + *unsafe { self.init.get_mut_unchecked() } = true; + Ok(()) + } + }) + } + + // Safety: + // dealloc can only be called by dropping Box + // which can happen only if already initialized + // thus it is safe to assume init == true + // + // not accissible from user code + #[inline] + fn dealloc(&self, index: usize) { + interrupt_free(|| unsafe { + *self.free.get_mut_unchecked().get_unchecked_mut(index) = + *self.head.get_mut_unchecked(); + *self.head.get_mut_unchecked() = index; + }); + } + + #[inline] + pub fn alloc(&'static self) -> Result<Box<T, S>, Error> { + // this check does not need to be inside of critical section + // as initialization is monotonic, cannot be undone + if !unsafe { *self.init.get_mut_unchecked() } { + Err(Error::Uninitialized) + } else { + interrupt_free(|| unsafe { + let index = *self.head.get_mut_unchecked(); + *self.head.get_mut_unchecked() = + *self.free.get_mut_unchecked().get_unchecked(index); + if *self.head.get_mut_unchecked() > S { + Err(Error::OutOfMemory) + } else { + Ok(Box { + index, + allocator: self, + }) + } + }) + } + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index fb3a7bd9a7f5c8b95d02768a05273fd75a7daecf..0000000000000000000000000000000000000000 --- a/src/main.rs +++ /dev/null @@ -1,134 +0,0 @@ -use core::cell::UnsafeCell; -use core::mem; -use core::ops::{Deref, DerefMut, Drop}; - -/// Internal replacement for `static mut T` -// TODO: Decide name and location. -pub struct RacyCell<T>(UnsafeCell<T>); - -impl<T> RacyCell<T> { - /// Create a RacyCell - pub const fn new(value: T) -> Self { - RacyCell(UnsafeCell::new(value)) - } - - /// Get &mut T - pub unsafe fn get_mut_unchecked(&self) -> &mut T { - &mut *self.0.get() - } -} - -// The type wrapped need to be Sync for RacyCell<T> to be Sync -//unsafe impl<T> Sync for RacyCell<T> where T: Sync {} -unsafe impl<T> Sync for RacyCell<T> {} - -struct Box<T: 'static + Sized, const S: usize> { - // data: &'static mut T, - index: usize, - allocator: &'static PoolMan<T, S>, -} - -impl<T: 'static, const S: usize> Drop for Box<T, S> { - fn drop(&mut self) { - self.allocator.dealloc(self.index) - } -} - -impl<T: 'static, const S: usize> Deref for Box<T, S> { - type Target = T; - fn deref(&self) -> &T { - unsafe { &self.allocator.data.get_mut_unchecked()[self.index] } - } -} - -impl<T: 'static, const S: usize> DerefMut for Box<T, S> { - // type Target = T; - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut self.allocator.data.get_mut_unchecked()[self.index] } - } -} - -struct PoolMan<T: 'static, const S: usize> { - data: RacyCell<[T; S]>, - free: RacyCell<[usize; S]>, - head: RacyCell<usize>, - init: RacyCell<bool>, -} - -impl<T, const S: usize> PoolMan<T, S> -where - T: Sized, -{ - const fn new(v: [T; S]) -> Self - where - T: Sized, - { - Self { - data: RacyCell::new(v), - free: RacyCell::new([0; S]), - head: RacyCell::new(0), - init: RacyCell::new(false), - } - } - - fn init(&self) { - let free = unsafe { self.free.get_mut_unchecked() }; - for (index, value) in free.iter_mut().enumerate() { - *value = index + 1; - } - *unsafe { self.init.get_mut_unchecked() } = true; - - println!("init : free {:?}", unsafe { self.free.get_mut_unchecked() }); - } - - fn dealloc(&self, index: usize) { - unsafe { - if !*self.init.get_mut_unchecked() { - panic!(); - } - self.free.get_mut_unchecked()[index] = *self.head.get_mut_unchecked(); - *self.head.get_mut_unchecked() = index; - } - println!("dealloc index {}", index) - } - - fn alloc(&'static self) -> Result<Box<T, S>, ()> { - unsafe { - if !*self.init.get_mut_unchecked() { - panic!(); - } - let index = *self.head.get_mut_unchecked(); - println!("index {}", self.head.get_mut_unchecked()); - println!("head {}", self.head.get_mut_unchecked()); - *self.head.get_mut_unchecked() = self.free.get_mut_unchecked()[index]; - println!("new head {}", self.head.get_mut_unchecked()); - if *self.head.get_mut_unchecked() > S { - Err(()) - } else { - Ok(Box { - index, - allocator: self, - }) - } - } - } -} - -static P: PoolMan<u32, 2> = PoolMan::new([0; 2]); -fn main() { - P.init(); - { - let e = P.alloc().unwrap(); - println!("e {}", *e); - mem::drop(e); - let mut e1 = P.alloc().unwrap(); - println!("e1 {}", *e1); - *e1 = 2; - println!("e1 {}", *e1); - - let e2 = P.alloc().unwrap(); - println!("e2 {}", *e2); - } - let e = P.alloc().unwrap(); - println!("e {}", *e); -}