use core::ffi::c_void;
use core::fmt::{Debug, Display, Formatter};
use core::ops::Deref;
use core::ptr::null_mut;
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use alloc::boxed::Box;
use alloc::sync::Arc;
use super::ffi::task::INVALID;
use super::ffi::{ TaskStatus, ThreadHandle, pdPASS, pdTRUE, vTaskDelete, vTaskGetInfo, vTaskResume, vTaskSuspend, xTaskCreate, xTaskGetCurrentTaskHandle};
use super::types::{StackType, UBaseType, BaseType, TickType};
use crate::traits::ThreadState::*;
use crate::os::ThreadSimpleFnPtr;
use crate::traits::{ThreadFn, ThreadParam, ThreadFnPtr, ThreadNotification, ThreadMetadata, ToTick, ToPriority};
use crate::traits::{MAX_TASK_NAME_LEN, SemaphoreFn};
use crate::freertos::semaphore::Semaphore;
use crate::utils::{Bytes, DoublePtr, Error, MAX_DELAY, Result};
struct JoinState {
done: Semaphore,
finished: AtomicBool,
joined: AtomicBool,
retval: AtomicPtr<c_void>,
}
impl JoinState {
fn new() -> Result<Arc<Self>> {
Ok(Arc::new(Self {
done: Semaphore::new(1, 0)?,
finished: AtomicBool::new(false),
joined: AtomicBool::new(false),
retval: AtomicPtr::new(null_mut()),
}))
}
fn publish(&self, ret: Result<ThreadParam>) {
self.retval
.store(Box::into_raw(Box::new(ret)) as *mut c_void, Ordering::Release);
self.finished.store(true, Ordering::Release);
self.done.signal();
}
fn discard_retval(&self) {
let raw = self.retval.swap(null_mut(), Ordering::Acquire);
if !raw.is_null() {
drop(unsafe { Box::from_raw(raw as *mut Result<ThreadParam>) });
}
}
}
impl Drop for JoinState {
fn drop(&mut self) {
self.discard_retval();
}
}
impl From<(ThreadHandle,TaskStatus)> for ThreadMetadata {
fn from(status: (ThreadHandle, TaskStatus)) -> Self {
let state = match status.1.eCurrentState {
0 => Running,
1 => Ready,
2 => Blocked,
3 => Suspended,
4 => Deleted,
_ => Invalid,
};
ThreadMetadata {
thread: status.0,
name: Bytes::from_char_ptr(status.1.pcTaskName),
stack_depth: 0,
priority: status.1.uxBasePriority,
thread_number: status.1.xTaskNumber,
state,
current_priority: status.1.uxCurrentPriority,
base_priority: status.1.uxBasePriority,
run_time_counter: status.1.ulRunTimeCounter,
stack_high_water_mark: status.1.usStackHighWaterMark,
}
}
}
#[derive(Clone)]
pub struct Thread {
handle: ThreadHandle,
name: Bytes<MAX_TASK_NAME_LEN>,
stack_depth: StackType,
priority: UBaseType,
callback: Option<Arc<ThreadFnPtr>>,
param: Option<ThreadParam>,
join_state: Option<Arc<JoinState>>,
}
unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}
impl Thread {
pub fn new(name: &str, stack_depth: StackType, priority: UBaseType) -> Self
{
Self {
handle: null_mut(),
name: Bytes::from_str(name),
stack_depth,
priority,
callback: None,
param: None,
join_state: None,
}
}
pub fn new_with_handle(handle: ThreadHandle, name: &str, stack_depth: StackType, priority: UBaseType) -> Result<Self> {
if handle.is_null() {
return Err(Error::NullPtr);
}
Ok(Self {
handle,
name: Bytes::from_str(name),
stack_depth,
priority,
callback: None,
param: None,
join_state: None,
})
}
pub fn new_with_to_priority(name: &str, stack_depth: StackType, priority: impl ToPriority) -> Self
{
Self {
handle: null_mut(),
name: Bytes::from_str(name),
stack_depth,
priority: priority.to_priority(),
callback: None,
param: None,
join_state: None,
}
}
pub fn new_with_handle_and_to_priority(handle: ThreadHandle, name: &str, stack_depth: StackType, priority: impl ToPriority) -> Result<Self> {
if handle.is_null() {
return Err(Error::NullPtr);
}
Ok(Self {
handle,
name: Bytes::from_str(name),
stack_depth,
priority: priority.to_priority(),
callback: None,
param: None,
join_state: None,
})
}
pub fn get_metadata_from_handle(handle: ThreadHandle) -> ThreadMetadata {
let mut status = TaskStatus::default();
unsafe {
vTaskGetInfo(handle, &mut status, pdTRUE, INVALID);
}
ThreadMetadata::from((handle, status))
}
pub fn get_metadata(thread: &Thread) -> ThreadMetadata {
if thread.is_null() {
return ThreadMetadata {
name: thread.name,
stack_depth: thread.stack_depth,
priority: thread.priority,
current_priority: thread.priority,
base_priority: thread.priority,
..ThreadMetadata::default()
};
}
Self::get_metadata_from_handle(thread.handle)
}
#[inline]
pub fn wait_notification_with_to_tick(&self, bits_to_clear_on_entry: u32, bits_to_clear_on_exit: u32 , timeout_ticks: impl ToTick) -> Result<u32> {
if self.is_null() {
return Err(Error::NullPtr);
}
self.wait_notification(bits_to_clear_on_entry, bits_to_clear_on_exit, timeout_ticks.to_ticks())
}
}
unsafe extern "C" fn callback_c_wrapper(param_ptr: *mut c_void) {
if param_ptr.is_null() {
return;
}
let mut thread_instance: Box<Thread> = unsafe { Box::from_raw(param_ptr as *mut _) };
thread_instance.as_mut().handle = unsafe { xTaskGetCurrentTaskHandle() };
let join_state = thread_instance.join_state.clone();
let param_arc: Option<ThreadParam> = thread_instance
.param
.clone();
let ret = if let Some(callback) = &thread_instance.callback.clone() {
callback(thread_instance, param_arc)
} else {
Err(Error::NullPtr)
};
if let Some(join_state) = join_state {
join_state.publish(ret);
}
unsafe { vTaskDelete( xTaskGetCurrentTaskHandle() ); }
}
unsafe extern "C" fn simple_callback_wrapper(param_ptr: *mut c_void) {
if param_ptr.is_null() {
return;
}
let payload: Box<(Arc<ThreadSimpleFnPtr>, Arc<JoinState>)> =
unsafe { Box::from_raw(param_ptr as *mut _) };
let (func, join_state) = *payload;
let ret = func();
join_state.publish(ret);
unsafe { vTaskDelete( xTaskGetCurrentTaskHandle()); }
}
impl Thread {
fn is_finished(&self) -> bool {
self.join_state
.as_ref()
.is_some_and(|state| state.finished.load(Ordering::Acquire))
}
}
impl ThreadFn for Thread {
fn is_null(&self) -> bool {
self.handle.is_null()
}
fn spawn<F>(&mut self, param: Option<ThreadParam>, callback: F) -> Result<Self>
where
F: Fn(Box<dyn ThreadFn>, Option<ThreadParam>) -> Result<ThreadParam>,
F: Send + Sync + 'static {
let mut handle: ThreadHandle = null_mut();
let func: Arc<ThreadFnPtr> = Arc::new(callback);
self.callback = Some(func);
self.param = param.clone();
let join_state = JoinState::new()?;
self.join_state = Some(join_state.clone());
let boxed_thread = Box::new(self.clone());
let ret = unsafe {
xTaskCreate(
Some(super::thread::callback_c_wrapper),
self.name.as_cstr().as_ptr(),
self.stack_depth,
Box::into_raw(boxed_thread) as *mut _,
self.priority,
&mut handle,
)
};
if ret != pdPASS {
self.join_state = None;
return Err(Error::OutOfMemory)
}
Ok(Self {
handle,
callback: self.callback.clone(),
param,
join_state: Some(join_state),
..self.clone()
})
}
fn spawn_simple<F>(&mut self, callback: F) -> Result<Self>
where
F: Fn() -> Result<ThreadParam> + Send + Sync + 'static,
{
let func: Arc<ThreadSimpleFnPtr> = Arc::new(callback);
let join_state = JoinState::new()?;
let boxed_func = Box::new((func, join_state.clone()));
let mut handle: ThreadHandle = null_mut();
let ret = unsafe {
xTaskCreate(
Some(simple_callback_wrapper),
self.name.as_cstr().as_ptr(),
self.stack_depth,
Box::into_raw(boxed_func) as *mut _,
self.priority,
&mut handle,
)
};
if ret != pdPASS {
return Err(Error::OutOfMemory);
}
Ok(Self {
handle,
join_state: Some(join_state),
..self.clone()
})
}
fn delete(&self) {
if self.is_null() {
return;
}
if self.is_finished() {
return;
}
unsafe { vTaskDelete( self.handle ); }
}
fn suspend(&self) {
if !self.is_null() && !self.is_finished() {
unsafe { vTaskSuspend( self.handle ); }
}
}
fn resume(&self) {
if !self.is_null() && !self.is_finished() {
unsafe { vTaskResume( self.handle ); }
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn join(&self, ret_val: DoublePtr) -> Result<i32> {
if self.is_null() {
return Err(Error::NullPtr);
}
let Some(join_state) = &self.join_state else {
return Err(Error::TaskNotFound);
};
if join_state.joined.swap(true, Ordering::AcqRel) {
return Err(Error::TaskNotFound);
}
join_state.done.wait(MAX_DELAY);
let raw = join_state.retval.swap(null_mut(), Ordering::Acquire);
if !ret_val.is_null() {
unsafe { *ret_val = raw; }
} else if !raw.is_null() {
drop(unsafe { Box::from_raw(raw as *mut Result<ThreadParam>) });
}
Ok(0)
}
fn get_metadata(&self) -> ThreadMetadata {
let mut status = TaskStatus::default();
unsafe {
vTaskGetInfo(self.handle, &mut status, pdTRUE, INVALID);
}
ThreadMetadata::from((self.handle, status))
}
fn get_current() -> Self {
let handle = unsafe { xTaskGetCurrentTaskHandle() };
let metadata = Self::get_metadata_from_handle(handle);
Self {
handle,
name: metadata.name.clone(),
stack_depth: metadata.stack_depth,
priority: metadata.priority,
callback: None,
param: None,
join_state: None,
}
}
fn notify(&self, notification: ThreadNotification) -> Result<()> {
if self.is_null() {
return Err(Error::NullPtr);
}
let (action, value) = notification.into();
let ret = xTaskNotify!(
self.handle,
value,
action
);
if ret != pdPASS {
Err(Error::QueueFull)
} else {
Ok(())
}
}
fn notify_from_isr(&self, notification: ThreadNotification, higher_priority_task_woken: &mut BaseType) -> Result<()> {
if self.is_null() {
return Err(Error::NullPtr);
}
let (action, value) = notification.into();
let ret = xTaskNotifyFromISR!(
self.handle,
value,
action,
higher_priority_task_woken
);
if ret != pdPASS {
Err(Error::QueueFull)
} else {
Ok(())
}
}
fn wait_notification(&self, bits_to_clear_on_entry: u32, bits_to_clear_on_exit: u32 , timeout_ticks: TickType) -> Result<u32> {
if self.is_null() {
return Err(Error::NullPtr);
}
let mut notification_value: u32 = 0;
let ret = xTaskNotifyWait!(
bits_to_clear_on_entry,
bits_to_clear_on_exit,
&mut notification_value,
timeout_ticks
);
if ret == pdTRUE {
Ok(notification_value)
} else {
Err(Error::Timeout)
}
}
}
impl Deref for Thread {
type Target = ThreadHandle;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl Debug for Thread {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Thread")
.field("handle", &self.handle)
.field("name", &self.name)
.field("stack_depth", &self.stack_depth)
.field("priority", &self.priority)
.field("callback", &self.callback.as_ref().map(|_| "Some(...)"))
.field("param", &self.param)
.finish()
}
}
impl Display for Thread {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Thread {{ handle: {:?}, name: {}, priority: {}, stack_depth: {} }}", self.handle, self.name, self.priority, self.stack_depth)
}
}