rt/rust/src/task.rs

138 lines
4.4 KiB
Rust

use core::{
cell::UnsafeCell,
ffi::{c_void, CStr},
marker::PhantomPinned,
mem::zeroed,
panic::{RefUnwindSafe, UnwindSafe},
ptr::null_mut,
str,
};
use crate::{
bindings::{
rt_context_init, rt_syscall_op, rt_syscall_pend, rt_syscall_push, rt_syscall_record,
rt_task, rt_task_drop_privilege, rt_task_exit, rt_task_name, rt_task_sleep,
rt_task_sleep_periodic, rt_task_state, rt_task_yield, RT_STACK_MIN,
},
list::list_init,
tick::Utick,
};
pub const STACK_MIN: usize = RT_STACK_MIN as usize;
pub struct Task {
t: UnsafeCell<rt_task>,
_pin_marker: PhantomPinned,
}
unsafe impl Send for Task {}
unsafe impl Sync for Task {}
impl UnwindSafe for Task {}
impl RefUnwindSafe for Task {}
pub fn yield_() {
unsafe { rt_task_yield() }
}
pub fn sleep(ticks: Utick) {
unsafe { rt_task_sleep(ticks) }
}
pub fn sleep_periodic(last_wake_tick: &mut Utick, period: Utick) {
unsafe { rt_task_sleep_periodic(last_wake_tick, period) }
}
pub fn exit() -> ! {
unsafe { rt_task_exit() }
}
pub fn name() -> &'static str {
unsafe { str::from_utf8_unchecked(CStr::from_ptr(rt_task_name()).to_bytes()) }
}
pub fn drop_privilege() {
unsafe { rt_task_drop_privilege() }
}
impl Task {
pub const fn init(task: &'static Task, name: &'static CStr, priority: u32) -> Task {
let t = unsafe { &*(task.t.get() as *const rt_task) };
Task {
t: UnsafeCell::new(rt_task {
list: list_init(&t.list),
sleep_list: list_init(&t.sleep_list),
ctx: null_mut(),
priority,
base_priority: priority,
state: rt_task_state::RT_TASK_STATE_INIT,
wake_tick: 0,
wait_list_head: null_mut(),
blocking_mutex: null_mut(),
timeout_ptr: unsafe { zeroed() },
mutex_list: list_init(&t.mutex_list),
name: name.as_ptr(),
}),
_pin_marker: PhantomPinned,
}
}
pub fn ready(
&mut self,
ready_record: &mut rt_syscall_record,
f: extern "C" fn(),
stack: &mut [u8],
) {
let t = self.t.get_mut();
unsafe {
t.ctx = rt_context_init(Some(f), stack.as_mut_ptr() as *mut c_void, stack.len());
ready_record.args.task_ready.task = t;
ready_record.op = rt_syscall_op::RT_SYSCALL_TASK_READY;
rt_syscall_push(ready_record);
rt_syscall_pend();
}
}
}
#[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")
};
}
#[macro_export]
macro_rules! task {
($fn: ident $params: tt, $stack_size: expr, $priority: expr) => {
const _: () = {
$crate::paste::paste! {
static mut [< $fn:upper _TASK >]: rt::task::Task = rt::task::Task::init(
unsafe { &[< $fn:upper _TASK >] },
unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked($crate::task_name!($fn $params).as_bytes()) },
$priority,
);
static mut [< $fn:upper _TASK_STACK >]: [u8; $stack_size] = [0u8; $stack_size];
extern fn [< $fn _c >]() {
$fn $params
}
/* Use a task ready syscall in the constructor rather than directly adding the task
* to the ready list because the ready list might not be initialized yet. If rust
* gains support for constructor priorities then this could be used instead. */
static mut [< $fn:upper _READY_RECORD >]: $crate::rt_syscall_record = unsafe { core::mem::zeroed() };
unsafe extern fn [< $fn _task_init >]() {
[< $fn:upper _TASK >].ready(&mut [< $fn:upper _READY_RECORD >], [< $fn _c >], [< $fn:upper _TASK_STACK >].as_mut_slice());
}
#[used]
#[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = ".init_array")]
static [< $fn:upper _TASK_INIT >]: unsafe extern fn() = [< $fn _task_init >];
}
};
};
($fn: ident, $stack_size: expr, $priority: expr) => {
rt::task!($fn(), $stack_size, $priority);
};
}