rt 0.19.1

A real-time operating system capable of full preemption
Documentation
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 {
    /// Initialize a new `Notify` with the given value.
    ///
    /// # Safety
    ///
    /// This must be called with a pointer to a `static` `Notify` and be used to initialize that
    /// same `Notify`. Users should use the `rt::sync::notify!` macro to create a `Notify` rather than
    /// call `Notify::init` directly.
    #[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);
    }
}