use core::{
cell::UnsafeCell,
mem::MaybeUninit,
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{
bindings::{
rt_notify, rt_notify_or, rt_notify_post, rt_notify_set, rt_notify_timedwait,
rt_notify_timedwait_clear, rt_notify_trywait, rt_notify_trywait_clear, rt_notify_wait,
rt_notify_wait_clear,
},
ptr_macros::{ptr_to_field, ptr_to_field_mut},
sync::sem::c_sem_init,
tick::Utick,
};
#[repr(transparent)]
pub struct Notify {
note: UnsafeCell<rt_notify>,
}
unsafe impl Send for Notify {}
unsafe impl Sync for Notify {}
impl UnwindSafe for Notify {}
impl RefUnwindSafe for Notify {}
impl Notify {
#[must_use]
pub const unsafe fn init(this: *const Self, value: u32) -> Notify {
let note = UnsafeCell::raw_get(ptr_to_field!(this, note));
Notify {
note: UnsafeCell::new(rt_notify {
value,
sem: c_sem_init(ptr_to_field_mut!(note, sem), 0, 1),
}),
}
}
#[inline]
pub fn post(&self) {
unsafe { rt_notify_post(self.note.get()) }
}
#[inline]
pub fn or(&self, v: u32) {
unsafe { rt_notify_or(self.note.get(), v) }
}
#[inline]
pub fn set(&self, v: u32) {
unsafe { rt_notify_set(self.note.get(), v) }
}
#[inline]
pub fn wait(&self) -> u32 {
unsafe { rt_notify_wait(self.note.get()) }
}
#[inline]
pub fn wait_clear(&self, c: u32) -> u32 {
unsafe { rt_notify_wait_clear(self.note.get(), c) }
}
#[inline]
pub fn try_wait(&self) -> Option<u32> {
let mut v = MaybeUninit::<u32>::uninit();
if unsafe { rt_notify_trywait(self.note.get(), v.as_mut_ptr()) } {
Some(unsafe { v.assume_init() })
} else {
None
}
}
#[inline]
pub fn try_wait_clear(&self, c: u32) -> Option<u32> {
let mut v = MaybeUninit::<u32>::uninit();
if unsafe { rt_notify_trywait_clear(self.note.get(), v.as_mut_ptr(), c) } {
Some(unsafe { v.assume_init() })
} else {
None
}
}
#[inline]
pub fn timed_wait(&self, ticks: Utick) -> Option<u32> {
let mut v = MaybeUninit::<u32>::uninit();
if unsafe { rt_notify_timedwait(self.note.get(), v.as_mut_ptr(), ticks) } {
Some(unsafe { v.assume_init() })
} else {
None
}
}
#[inline]
pub fn timed_wait_clear(&self, c: u32, ticks: Utick) -> Option<u32> {
let mut v = MaybeUninit::<u32>::uninit();
if unsafe { rt_notify_timedwait_clear(self.note.get(), v.as_mut_ptr(), c, ticks) } {
Some(unsafe { v.assume_init() })
} else {
None
}
}
}
#[macro_export]
macro_rules! notify {
($name: ident, $value: expr) => {
static $name: $crate::sync::Notify = {
let ptr = &raw const $name;
let value = $value;
unsafe { $crate::sync::Notify::init(ptr, value) }
};
};
($name: ident) => {
$crate::notify!($name, 0);
};
}
#[cfg(test)]
mod tests {
#[test]
fn fast_path() {
notify!(NOTIFY);
NOTIFY.post();
assert_eq!(NOTIFY.wait(), 0);
}
#[test]
fn ops() {
notify!(NOTIFY);
NOTIFY.or(1);
assert_eq!(NOTIFY.wait(), 1);
NOTIFY.or(2);
assert_eq!(NOTIFY.wait(), 3);
NOTIFY.set(8);
assert_eq!(NOTIFY.wait(), 8);
}
#[test]
fn clear() {
notify!(NOTIFY);
NOTIFY.or(u32::MAX);
assert_eq!(NOTIFY.wait_clear(0xFF), u32::MAX);
NOTIFY.post();
assert_eq!(NOTIFY.wait(), 0xFFFFFF00);
}
}