215 lines
5.8 KiB
Rust
215 lines
5.8 KiB
Rust
use core::{
|
|
cell::UnsafeCell,
|
|
fmt::{self, Debug, Display, Write},
|
|
mem::MaybeUninit,
|
|
ops::Deref,
|
|
};
|
|
|
|
use embassy_futures::yield_now;
|
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
use embassy_time::{Duration, Timer};
|
|
use msgpck::{Marker, MsgPack, PackErr, Piece};
|
|
use portable_atomic::{AtomicU32, Ordering};
|
|
|
|
use crate::rgb::Rgb;
|
|
|
|
pub type CS = CriticalSectionRawMutex;
|
|
|
|
#[derive()]
|
|
pub struct SwapCell<T> {
|
|
// active_slot: 1 bit
|
|
// readers: 31 bits
|
|
entries: AtomicU32,
|
|
exits: AtomicU32,
|
|
|
|
slot0: UnsafeCell<MaybeUninit<T>>,
|
|
slot1: UnsafeCell<MaybeUninit<T>>,
|
|
}
|
|
|
|
pub struct SwapCellWrite<T: 'static> {
|
|
cell: &'static SwapCell<T>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct SwapCellRead<T: 'static> {
|
|
cell: &'static SwapCell<T>,
|
|
}
|
|
|
|
pub struct SwapCellGuard<'a, T> {
|
|
cell: &'a SwapCell<T>,
|
|
slot: &'a T,
|
|
}
|
|
|
|
const SLOT_MASK: u32 = 0x80000000;
|
|
const ENTRIES_MASK: u32 = 0x7FFFFFFF;
|
|
|
|
impl<T> SwapCell<T> {
|
|
pub const fn new(initial: T) -> Self {
|
|
SwapCell {
|
|
entries: AtomicU32::new(0),
|
|
exits: AtomicU32::new(0),
|
|
slot0: UnsafeCell::new(MaybeUninit::new(initial)),
|
|
slot1: UnsafeCell::new(MaybeUninit::uninit()),
|
|
}
|
|
}
|
|
|
|
pub fn split(&'static mut self) -> (SwapCellRead<T>, SwapCellWrite<T>) {
|
|
(SwapCellRead { cell: self }, SwapCellWrite { cell: self })
|
|
}
|
|
}
|
|
|
|
impl<T> SwapCellWrite<T> {
|
|
pub async fn write(&mut self, t: T) {
|
|
let x = self.cell.entries.load(Ordering::SeqCst);
|
|
let active_slot = (x & SLOT_MASK).rotate_left(1) as usize;
|
|
|
|
let mut slots = [&self.cell.slot0, &self.cell.slot1];
|
|
slots.rotate_left(active_slot);
|
|
let [active_slot, inactive_slot] = slots;
|
|
|
|
// SAFETY: no one elase is allowed to touch the inactive slot.
|
|
unsafe { inactive_slot.get().write(MaybeUninit::new(t)) };
|
|
|
|
// swap active/inactive slots
|
|
let x = self.cell.entries.fetch_xor(SLOT_MASK, Ordering::SeqCst);
|
|
let (_active_slot, inactive_slot) = (inactive_slot, active_slot);
|
|
|
|
// wait until there are no more readers in the previously active slot
|
|
let entries = x & ENTRIES_MASK;
|
|
loop {
|
|
let exits = self.cell.exits.load(Ordering::SeqCst);
|
|
if exits >= entries {
|
|
break;
|
|
}
|
|
|
|
yield_now().await;
|
|
}
|
|
|
|
// drop the now inactive slot
|
|
// SAFETY: we waited until everyone else stopped using this slot.
|
|
unsafe { inactive_slot.get().drop_in_place() };
|
|
}
|
|
}
|
|
|
|
impl<T> SwapCellRead<T> {
|
|
pub fn read(&self) -> SwapCellGuard<'_, T> {
|
|
let x = self.cell.entries.fetch_add(1, Ordering::SeqCst);
|
|
|
|
let entries = x & ENTRIES_MASK;
|
|
debug_assert!(entries < ENTRIES_MASK, "SwapCell overflowed");
|
|
|
|
let slot = (x & SLOT_MASK).rotate_left(1) as usize;
|
|
let slot = [&self.cell.slot0, &self.cell.slot1][slot];
|
|
let slot = unsafe { &*slot.get() };
|
|
let slot = unsafe { slot.assume_init_ref() };
|
|
|
|
SwapCellGuard {
|
|
cell: self.cell,
|
|
slot,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Drop for SwapCellGuard<'_, T> {
|
|
fn drop(&mut self) {
|
|
self.cell.exits.fetch_add(1, Ordering::SeqCst);
|
|
}
|
|
}
|
|
|
|
impl<T> Deref for SwapCellGuard<'_, T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
self.slot
|
|
}
|
|
}
|
|
|
|
pub async fn stall() -> ! {
|
|
loop {
|
|
Timer::after(Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
|
|
/// Input a value 0 to 255 to get a color value
|
|
// The colours are a transition r - g - b - back to r.
|
|
pub fn wheel(mut wheel_pos: u8) -> Rgb {
|
|
wheel_pos = 255 - wheel_pos;
|
|
if wheel_pos < 85 {
|
|
Rgb::new(255 - wheel_pos * 3, 0, wheel_pos * 3)
|
|
} else if wheel_pos < 170 {
|
|
wheel_pos -= 85;
|
|
Rgb::new(0, wheel_pos * 3, 255 - wheel_pos * 3)
|
|
} else {
|
|
wheel_pos -= 170;
|
|
Rgb::new(wheel_pos * 3, 255 - wheel_pos * 3, 0)
|
|
}
|
|
}
|
|
|
|
/// Calculate the length of the formatted string of a type that impls Display.
|
|
pub fn display_len(t: &impl Display) -> usize {
|
|
// impl fmt::Write for a dummy struct that just increments a length
|
|
struct Write<'a>(&'a mut usize);
|
|
impl fmt::Write for Write<'_> {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
*self.0 += s.len();
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
let mut n = 0;
|
|
|
|
let mut w = Write(&mut n);
|
|
write!(&mut w, "{}", t).expect("Write impl is infallible");
|
|
|
|
n
|
|
}
|
|
|
|
/// Wrapper type that impls [MsgPack] for `T: Display`, by serializing `T` as a msgpck string.
|
|
pub struct DisplayPack<T>(pub T);
|
|
impl<T: Display> MsgPack for DisplayPack<T> {
|
|
fn pack(&self) -> impl Iterator<Item = msgpck::Piece<'_>> {
|
|
"TODO".pack()
|
|
//let len = display_len(&self.0) as u32;
|
|
|
|
//[msgpck::Marker::Str32.into(), len.into()]
|
|
// .into_iter()
|
|
// .chain(iter::from_fn(move || None)) // TODO
|
|
}
|
|
|
|
fn pack_with_writer(&self, w: &mut dyn msgpck::Write) -> Result<usize, PackErr> {
|
|
let mut n = 0;
|
|
|
|
let str_len = display_len(&self.0);
|
|
|
|
for bytes in [
|
|
Piece::from(Marker::Str32).as_bytes(),
|
|
Piece::from(str_len as u32).as_bytes(),
|
|
] {
|
|
w.write_all(bytes)?;
|
|
n += bytes.len();
|
|
}
|
|
|
|
// this is advanced stupid.
|
|
struct Write<'a>(&'a mut dyn msgpck::Write);
|
|
impl fmt::Write for Write<'_> {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
self.0
|
|
.write_all(s.as_bytes())
|
|
.map_err(|_| fmt::Error::default())?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
write!(&mut Write(w), "{}", self.0).map_err(|_| PackErr::BufferOverflow)?;
|
|
n += str_len;
|
|
|
|
Ok(n)
|
|
}
|
|
}
|
|
|
|
impl<T: Debug> Debug for DisplayPack<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
Debug::fmt(&self.0, f)
|
|
}
|
|
}
|