use core::cell::{Cell, UnsafeCell};
use core::mem::MaybeUninit;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
pub struct Signal<M, T>
where
M: RawMutex,
{
inner: Mutex<M, SignalInner<T>>,
}
struct SignalInner<T> {
value: UnsafeCell<MaybeUninit<T>>,
signaled: Cell<bool>,
}
impl<T> SignalInner<T> {
#[inline]
fn write(&self, val: T) {
unsafe { self.value.get().write(MaybeUninit::new(val)) };
self.signaled.set(true);
}
#[inline]
fn take(&self) -> T {
self.signaled.set(false);
unsafe { self.value.get().read().assume_init() }
}
#[inline]
fn is_signaled(&self) -> bool {
self.signaled.get()
}
}
impl<M, T> Signal<M, T>
where
M: RawMutex,
{
pub const fn new() -> Self {
Self {
inner: Mutex::new(SignalInner {
value: UnsafeCell::new(MaybeUninit::zeroed()),
signaled: Cell::new(false),
}),
}
}
}
impl<M, T> Default for Signal<M, T>
where
M: RawMutex,
{
fn default() -> Self {
Self::new()
}
}
impl<M, T> Signal<M, T>
where
M: RawMutex,
{
pub fn signal(&self, val: T) {
self.inner.lock(|signal| {
signal.write(val);
})
}
pub fn reset(&self) {
self.try_take();
}
pub fn try_take(&self) -> Option<T> {
self.inner.lock(|signal| {
if signal.is_signaled() {
Some(signal.take())
} else {
None
}
})
}
pub fn signaled(&self) -> bool {
self.inner.lock(|signal| signal.is_signaled())
}
}