rt 0.19.1

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

use crate::{
    bindings::{
        rt_context_init, rt_syscall_pendable, rt_syscall_record, rt_task, rt_task_drop_privilege,
        rt_task_exit, rt_task_init, rt_task_name, rt_task_sleep, rt_task_sleep_periodic,
        rt_task_state, rt_task_yield,
    },
    list::list_init,
    ptr_macros::{ptr_to_field, ptr_to_field_mut},
    tick::Utick,
};

#[repr(transparent)]
pub struct Task {
    pub(crate) task: UnsafeCell<rt_task>,
}

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

#[inline]
pub fn yield_now() {
    unsafe { rt_task_yield() }
}

#[inline]
pub fn sleep(ticks: Utick) {
    unsafe { rt_task_sleep(ticks) }
}

#[inline]
pub fn sleep_periodic(last_wake_tick: &mut Utick, period: Utick) {
    unsafe { rt_task_sleep_periodic(last_wake_tick, period) }
}

#[inline]
pub fn exit() -> ! {
    unsafe { rt_task_exit() }
}

#[inline]
pub fn name() -> &'static str {
    unsafe { str::from_utf8_unchecked(CStr::from_ptr(rt_task_name()).to_bytes()) }
}

#[inline]
pub fn drop_privilege() {
    unsafe { rt_task_drop_privilege() }
}

impl Task {
    /// Initialize a new `Task` with the given name and priority.
    ///
    /// # Safety
    ///
    /// This must be called with a pointer to a `static` `Task` and be used to initialize that same
    /// `Task`. Users should use the `rt::task!` macro to create a `Task` rather than call
    /// `Task::init` directly.
    #[must_use]
    pub const unsafe fn init(this: *const Self, name: &'static CStr, priority: u32) -> Task {
        let task = UnsafeCell::raw_get(ptr_to_field!(this, task));
        Task {
            task: UnsafeCell::new(rt_task {
                list: list_init(ptr_to_field_mut!(task, list)),
                sleep_list: list_init(ptr_to_field_mut!(task, sleep_list)),
                ctx: null_mut(),
                priority,
                base_priority: priority,
                state: rt_task_state::RT_TASK_STATE_READY,
                wake_tick: 0,
                #[cfg(feature = "task-mpu")]
                mpu_config: unsafe { zeroed() },
                blocker: unsafe { zeroed() },
                syscall_return: unsafe { zeroed() },
                mutex_list: list_init(ptr_to_field_mut!(task, mutex_list)),
                pending_function_record: rt_syscall_record {
                    next: null_mut(),
                    args: unsafe { zeroed() },
                    syscall: rt_syscall_pendable::RT_SYSCALL_PENDABLE_FUNCTION,
                    pending: unsafe { zeroed() },
                },
                fn_: unsafe { zeroed() },
                arg: 0,
                stack: null_mut(),
                stack_size: 0,
                name: name.as_ptr(),
                global_task_list: list_init(ptr_to_field_mut!(task, global_task_list)),
            }),
        }
    }

    pub fn ready(&'static self, f: extern "C" fn(), stack: *mut u8, stack_size: usize) {
        let t = self.task.get();
        let fnptr = f as usize;
        unsafe {
            (*t).ctx = rt_context_init(fnptr, 0, stack.cast(), stack_size);
            (*t).fn_.uintptr = fnptr;
            (*t).stack = stack.cast();
            (*t).stack_size = stack_size;
            rt_task_init(t);
        }
    }
}

#[macro_export]
macro_rules! task_name {
    ($fn: ident ()) => {
        core::concat!(core::stringify!($fn), "\0")
    };
    ($fn: ident $params: tt) => {
        core::concat!(core::stringify!($fn), core::stringify!($params), "\0")
    };
}

#[doc(hidden)]
#[macro_export]
#[cfg(feature = "task-mpu")]
macro_rules! mpu_config_init {
    ($fn: ident$(, $mpu_regions: expr)*) => { $crate::paste::paste! {
        let stack_region = $crate::mpu::Region::from_stack(
            &[< $fn:upper _TASK_STACK >]
        );
        $crate::mpu::task_mpu_config_init(
            &[< $fn:upper _TASK >],
            &[stack_region, $($mpu_regions),*],
        );
    }};
}

#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "task-mpu"))]
macro_rules! mpu_config_init {
    ($fn: ident$(, $mpu_regions: expr)*) => {};
}

#[doc(hidden)]
#[macro_export]
#[cfg(all(feature = "task-mpu", not(mpu_armv6m)))]
macro_rules! task_section {
    ($name: ident, $taskdef: item) => {
        #[unsafe(link_section = stringify!(.priv_data.$name))]
        $taskdef
    };
}

#[doc(hidden)]
#[macro_export]
#[cfg(not(all(feature = "task-mpu", not(mpu_armv6m))))]
macro_rules! task_section {
    ($name: ident, $taskdef: item) => {
        $taskdef
    };
}

#[macro_export]
macro_rules! task {
    ($fn: ident $params: tt, $stack_size: expr, $priority: expr$(, $mpu_regions: expr)*) => {
        const _: () = {
            use core::{concat, stringify, ffi::CStr};

            use $crate::{
                cell::SyncUnsafeCell,
                ctor,
                mpu_config_init,
                paste::paste,
                stack::Stack,
                task::Task,
                task_name,
            };

            paste! {
                $crate::task_section!(
                    [< $fn:upper _TASK >],
                    static [< $fn:upper _TASK >]: Task = {
                        let ptr = &raw const [< $fn:upper _TASK >];
                        let name_bytes = task_name!($fn $params).as_bytes();
                        let name = unsafe { CStr::from_bytes_with_nul_unchecked(name_bytes) };
                        let priority = $priority;
                        unsafe { Task::init(ptr, name, priority) }
                    };
                );

                #[cfg_attr(target_os = "none", unsafe(link_section = ".stack"))]
                static [< $fn:upper _TASK_STACK >]: Stack<{$stack_size}> = Stack::new();

                extern "C" fn [< $fn _task_fn >]() {
                    $fn $params
                }

                extern "C" fn [< $fn _task_init >]() {
                    mpu_config_init!($fn $(, $mpu_regions)*);
                    let ptr = SyncUnsafeCell::raw_get(&raw const [< $fn:upper _TASK_STACK >].buf);
                    [< $fn:upper _TASK >].ready([< $fn _task_fn >], ptr.cast(), $stack_size);
                }
                ctor!([< $fn:upper _TASK_INIT >], [< $fn _task_init >]);
            }
        };
    };

    ($fn: ident, $stack_size: expr, $priority: expr) => {
        rt::task!($fn(), $stack_size, $priority);
    };
}