pub mod local;
use alloc::{
boxed::Box,
string::{String, ToString},
};
use core::{ffi::CStr, hash::Hash, str::Utf8Error, time::Duration};
use snafu::Snafu;
use crate::{bail_on, map_errno};
pub fn spawn<F>(f: F) -> TaskHandle
where
F: FnOnce() + Send + 'static,
{
Builder::new().spawn(f).expect("Failed to spawn task")
}
fn spawn_inner<F: FnOnce() + Send + 'static>(
function: F,
priority: TaskPriority,
stack_depth: TaskStackDepth,
name: Option<&str>,
) -> Result<TaskHandle, SpawnError> {
let entrypoint = Box::new(TaskEntrypoint { function });
let name = alloc::ffi::CString::new(name.unwrap_or("<unnamed>"))
.unwrap()
.into_raw();
unsafe {
let task = bail_on!(
core::ptr::null(),
pros_sys::task_create(
Some(TaskEntrypoint::<F>::cast_and_call_external),
Box::into_raw(entrypoint).cast(),
priority as _,
stack_depth as _,
name,
)
);
_ = alloc::ffi::CString::from_raw(name);
Ok(TaskHandle { task })
}
}
#[derive(Debug, Clone)]
pub struct TaskHandle {
pub(crate) task: pros_sys::task_t,
}
unsafe impl Send for TaskHandle {}
impl Hash for TaskHandle {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.task.hash(state)
}
}
impl PartialEq for TaskHandle {
fn eq(&self, other: &Self) -> bool {
self.task == other.task
}
}
impl Eq for TaskHandle {}
impl TaskHandle {
pub fn pause(&self) {
unsafe {
pros_sys::task_suspend(self.task);
}
}
pub fn unpause(&self) {
unsafe {
pros_sys::task_resume(self.task);
}
}
pub fn set_priority(&self, priority: impl Into<u32>) {
unsafe {
pros_sys::task_set_priority(self.task, priority.into());
}
}
pub fn state(&self) -> TaskState {
unsafe { pros_sys::task_get_state(self.task).into() }
}
pub fn notify(&self) {
unsafe {
pros_sys::task_notify(self.task);
}
}
pub fn join(self) {
unsafe {
pros_sys::task_join(self.task);
}
}
pub fn abort(self) {
unsafe {
pros_sys::task_delete(self.task);
}
}
pub fn name(&self) -> Result<String, Utf8Error> {
unsafe {
let name = pros_sys::task_get_name(self.task);
let name_str = CStr::from_ptr(name);
Ok(name_str.to_str()?.to_string())
}
}
}
#[derive(Debug, Default)]
pub struct Builder<'a> {
name: Option<&'a str>,
priority: Option<TaskPriority>,
stack_depth: Option<TaskStackDepth>,
}
impl<'a> Builder<'a> {
pub fn new() -> Self {
Self::default()
}
pub const fn name(mut self, name: &'a str) -> Self {
self.name = Some(name);
self
}
pub const fn priority(mut self, priority: TaskPriority) -> Self {
self.priority = Some(priority);
self
}
pub const fn stack_depth(mut self, stack_depth: TaskStackDepth) -> Self {
self.stack_depth = Some(stack_depth);
self
}
pub fn spawn<F>(self, function: F) -> Result<TaskHandle, SpawnError>
where
F: FnOnce() + Send + 'static,
{
spawn_inner(
function,
self.priority.unwrap_or_default(),
self.stack_depth.unwrap_or_default(),
self.name,
)
}
}
#[derive(Debug)]
pub enum TaskState {
Running,
Ready,
Blocked,
Suspended,
Deleted,
Invalid,
}
impl From<u32> for TaskState {
fn from(value: u32) -> Self {
match value {
pros_sys::E_TASK_STATE_RUNNING => Self::Running,
pros_sys::E_TASK_STATE_READY => Self::Ready,
pros_sys::E_TASK_STATE_BLOCKED => Self::Blocked,
pros_sys::E_TASK_STATE_SUSPENDED => Self::Suspended,
pros_sys::E_TASK_STATE_DELETED => Self::Deleted,
pros_sys::E_TASK_STATE_INVALID => Self::Invalid,
_ => Self::Invalid,
}
}
}
#[repr(u32)]
#[derive(Debug, Default)]
pub enum TaskPriority {
High = 16,
#[default]
Default = 8,
Low = 1,
}
impl From<TaskPriority> for u32 {
fn from(val: TaskPriority) -> Self {
val as u32
}
}
#[repr(u32)]
#[derive(Debug, Default)]
pub enum TaskStackDepth {
#[default]
Default = 8192,
Low = 512,
}
struct TaskEntrypoint<F> {
function: F,
}
impl<F> TaskEntrypoint<F>
where
F: FnOnce(),
{
unsafe extern "C" fn cast_and_call_external(this: *mut core::ffi::c_void) {
let this = unsafe { Box::from_raw(this.cast::<Self>()) };
(this.function)()
}
}
#[derive(Debug, Snafu)]
pub enum SpawnError {
TCBNotCreated,
}
map_errno! {
SpawnError {
ENOMEM => SpawnError::TCBNotCreated,
}
}
pub fn delay(duration: Duration) {
unsafe { pros_sys::delay(duration.as_millis() as u32) }
}
#[derive(Debug)]
pub struct Interval {
last_unblock_time: u32,
}
impl Interval {
pub fn start() -> Self {
Self {
last_unblock_time: unsafe { pros_sys::millis() },
}
}
pub fn delay(&mut self, delta: Duration) {
let delta = delta.as_millis() as u32;
unsafe {
pros_sys::task_delay_until((&mut self.last_unblock_time) as *mut _, delta);
}
}
}
pub fn current() -> TaskHandle {
unsafe {
let task = pros_sys::task_get_current();
TaskHandle { task }
}
}
pub fn get_notification() -> u32 {
unsafe { pros_sys::task_notify_take(false, pros_sys::TIMEOUT_MAX) }
}
#[derive(Debug)]
pub struct SchedulerSuspendGuard {
_private: (),
}
impl Drop for SchedulerSuspendGuard {
fn drop(&mut self) {
unsafe {
pros_sys::rtos_resume_all();
}
}
}
#[must_use = "The scheduler will only remain suspended for the lifetime of the returned guard"]
pub unsafe fn suspend_all() -> SchedulerSuspendGuard {
unsafe { pros_sys::rtos_suspend_all() };
SchedulerSuspendGuard { _private: () }
}