use core::cell::UnsafeCell;
use critical_section::Mutex;
use zencan_common::AtomicCell;
#[derive(Debug)]
pub struct ObjectFlagSync {
inner: Mutex<UnsafeCell<ObjectFlagsInner>>,
}
impl Default for ObjectFlagSync {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
struct ObjectFlagsInner {
toggle: bool,
global_flag: bool,
}
impl ObjectFlagSync {
pub const fn new() -> Self {
Self {
inner: Mutex::new(UnsafeCell::new(ObjectFlagsInner {
toggle: false,
global_flag: false,
})),
}
}
pub fn toggle(&self) -> bool {
critical_section::with(|cs| {
let inner = self.inner.borrow(cs).get();
unsafe {
let global = (*inner).global_flag;
(*inner).global_flag = false;
(*inner).toggle = !(*inner).toggle;
global
}
})
}
pub fn get_flag(&self, setting: bool) -> bool {
critical_section::with(|cs| {
let inner = unsafe { &mut (*self.inner.borrow(cs).get()) };
inner.global_flag |= setting;
inner.toggle
})
}
}
#[allow(missing_debug_implementations)]
pub struct ObjectFlags<const N: usize> {
sync: &'static ObjectFlagSync,
flags0: AtomicCell<[u8; N]>,
flags1: AtomicCell<[u8; N]>,
}
pub trait ObjectFlagAccess {
fn set_flag(&self, sub: u8);
fn get_flag(&self, sub: u8) -> bool;
fn clear(&self);
}
impl<const N: usize> ObjectFlags<N> {
pub const fn new(sync: &'static ObjectFlagSync) -> Self {
Self {
sync,
flags0: AtomicCell::new([0; N]),
flags1: AtomicCell::new([0; N]),
}
}
}
impl<const N: usize> ObjectFlagAccess for ObjectFlags<N> {
fn set_flag(&self, sub: u8) {
if sub as usize >= N * 8 {
return;
}
let flags = if self.sync.get_flag(true) {
&self.flags0
} else {
&self.flags1
};
flags
.fetch_update(|mut f| {
f[sub as usize / 8] |= 1 << (sub & 7);
Some(f)
})
.unwrap();
}
fn get_flag(&self, sub: u8) -> bool {
if sub as usize >= N * 8 {
return false;
}
let flags = if self.sync.get_flag(false) {
&self.flags1.load()
} else {
&self.flags0.load()
};
flags[(sub / 8) as usize] & (1 << (sub & 7)) != 0
}
fn clear(&self) {
if self.sync.get_flag(false) {
self.flags1.store([0; N]);
} else {
self.flags0.store([0; N]);
}
}
}