rt 0.19.1

A real-time operating system capable of full preemption
Documentation
use core::{
    cell::UnsafeCell,
    mem::zeroed,
    panic::{RefUnwindSafe, UnwindSafe},
    ptr::null_mut,
};

use crate::{
    bindings::{
        RT_EVENT_WAIT_ALL, RT_EVENT_WAIT_NOCLEAR, RT_EVENT_WAIT_RESERVED, rt_event, rt_event_clear,
        rt_event_get, rt_event_set, rt_event_timedwait, rt_event_trywait, rt_event_wait,
        rt_syscall_args, rt_syscall_args_event_set, rt_syscall_pendable, rt_syscall_record,
    },
    list::list_init,
    ptr_macros::{ptr_to_field, ptr_to_field_mut},
    tick::Utick,
};

#[repr(transparent)]
pub struct Event {
    event: UnsafeCell<rt_event>,
}

unsafe impl Send for Event {}
unsafe impl Sync for Event {}
impl UnwindSafe for Event {}
impl RefUnwindSafe for Event {}

impl Event {
    /// Initialize a new `Event`.
    ///
    /// # Safety
    ///
    /// This must be called with a pointer to a `static` `Event` and be used to initialize that
    /// same `Event`. Users should use the `rt::sync::event!` macro to create a `Event` rather than
    /// call `Event::init` directly.
    #[must_use]
    pub const unsafe fn init(this: *const Self) -> Event {
        let event = UnsafeCell::raw_get(ptr_to_field!(this, event));
        Event {
            event: UnsafeCell::new(rt_event {
                bits: 0u32,
                wait_list: list_init(ptr_to_field_mut!(event, wait_list)),
                set_record: rt_syscall_record {
                    next: null_mut(),
                    args: unsafe {
                        let mut x: rt_syscall_args = zeroed();
                        x.event_set = rt_syscall_args_event_set { event };
                        x
                    },
                    syscall: rt_syscall_pendable::RT_SYSCALL_PENDABLE_EVENT_SET,
                    pending: unsafe { zeroed() },
                },
            }),
        }
    }

    #[inline]
    pub fn clear(&self, bits: u32) -> u32 {
        unsafe { rt_event_clear(self.event.get(), bits) }
    }

    #[inline]
    pub fn get(&self) -> u32 {
        unsafe { rt_event_get(self.event.get()) }
    }

    #[inline]
    pub fn set(&self, bits: u32) -> u32 {
        unsafe { rt_event_set(self.event.get(), bits) }
    }

    #[inline]
    pub fn wait(&self, wait: u32) -> u32 {
        unsafe { rt_event_wait(self.event.get(), wait) }
    }

    #[inline]
    pub fn try_wait(&self, wait: u32) -> u32 {
        unsafe { rt_event_trywait(self.event.get(), wait) }
    }

    #[inline]
    pub fn timed_wait(&self, wait: u32, ticks: Utick) -> u32 {
        unsafe { rt_event_timedwait(self.event.get(), wait, ticks) }
    }
}

#[inline]
pub const fn bits_match(bits: u32, wait: u32) -> bool {
    let waitbits = wait & !RT_EVENT_WAIT_RESERVED;
    let bmatch = bits & waitbits;
    if (wait & WAIT_ALL) != 0 {
        return bmatch == waitbits;
    }
    bmatch != 0
}

pub const WAIT_ALL: u32 = RT_EVENT_WAIT_ALL;
pub const WAIT_NOCLEAR: u32 = RT_EVENT_WAIT_NOCLEAR;

#[macro_export]
macro_rules! event {
    ($name: ident) => {
        static $name: $crate::sync::Event = {
            let ptr = &raw const $name;
            unsafe { $crate::sync::Event::init(ptr) }
        };
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn fast_path() {
        event!(EVENT);
        EVENT.set(1);
        assert_eq!(EVENT.wait(1), 1);
        assert_eq!(EVENT.try_wait(1), 0);
    }
}