use core::cell::UnsafeCell;
use core::ffi::{c_int, c_long, c_void};
use core::fmt::{Debug, Display, Formatter};
use core::ops::Deref;
use core::ptr::null_mut;
use core::time::Duration;
use std::collections::HashMap;
use alloc::sync::Arc;
use crate::os::{Mutex, MutexFn, MutexGuard, ThreadSimpleFnPtr};
use crate::posix::config::TICK_PERIOD_MS;
#[cfg(feature = "real_time")]
use crate::posix::ffi::{PTHREAD_EXPLICIT_SCHED, SCHED_FIFO, pthread_attr_setinheritsched, pthread_attr_setschedparam, pthread_attr_setschedpolicy, sched_param};
use crate::posix::ffi::{
__libc_current_sigrtmin, CLOCK_MONOTONIC, ETIMEDOUT, PTHREAD_ONCE_INIT, PTHREAD_STACK_MIN, clock_gettime, pthread_attr_init, pthread_attr_setstacksize, pthread_attr_t,
pthread_cond_broadcast, pthread_cond_destroy, pthread_cond_init, pthread_cond_t, pthread_cond_timedwait, pthread_cond_wait, pthread_condattr_init, pthread_condattr_setclock,
pthread_condattr_t, pthread_create, pthread_join, pthread_kill, pthread_once, pthread_once_t, pthread_self, pthread_setname_np, sigdelset, sigfillset, sigset_t, signal, sigsuspend,
timespec,
};
use crate::posix::types::{BaseType, StackType, ThreadHandle, TickType, UBaseType};
use crate::traits::{ThreadFn, ThreadFnPtr, ThreadMetadata, ThreadNotification, ThreadParam, ThreadState, ToPriority, ToTick};
use crate::traits::MAX_TASK_NAME_LEN;
use crate::utils::{Bytes, DoublePtr, Error, Result};
fn suspend_signal() -> c_int {
unsafe { __libc_current_sigrtmin() }
}
fn resume_signal() -> c_int {
suspend_signal() + 1
}
extern "C" fn suspend_signal_handler(_sig: c_int) {
let mut mask: sigset_t = Default::default();
unsafe {
sigfillset(&mut mask);
sigdelset(&mut mask, resume_signal());
sigsuspend(&mask);
}
}
extern "C" fn resume_signal_handler(_sig: c_int) {}
fn ensure_suspend_signal_handlers() {
static mut ONCE: pthread_once_t = PTHREAD_ONCE_INIT;
extern "C" fn init() {
unsafe {
signal(suspend_signal(), suspend_signal_handler as *const () as usize);
signal(resume_signal(), resume_signal_handler as *const () as usize);
}
}
unsafe {
pthread_once(&raw mut ONCE, Some(init));
}
}
struct RawCondvar(UnsafeCell<pthread_cond_t>);
unsafe impl Send for RawCondvar {}
unsafe impl Sync for RawCondvar {}
impl RawCondvar {
fn new() -> Self {
let mut attr: pthread_condattr_t = Default::default();
let mut cond: pthread_cond_t = Default::default();
unsafe {
pthread_condattr_init(&mut attr);
pthread_condattr_setclock(&mut attr, CLOCK_MONOTONIC);
pthread_cond_init(&mut cond, &attr);
}
Self(UnsafeCell::new(cond))
}
fn wait<T: ?Sized>(&self, guard: &MutexGuard<'_, T>) {
unsafe {
pthread_cond_wait(self.0.get(), guard.raw_handle());
}
}
fn wait_until<T: ?Sized>(&self, guard: &MutexGuard<'_, T>, deadline: timespec) -> bool {
unsafe { pthread_cond_timedwait(self.0.get(), guard.raw_handle(), &deadline) == ETIMEDOUT }
}
fn notify_all(&self) {
unsafe {
pthread_cond_broadcast(self.0.get());
}
}
}
impl Default for RawCondvar {
fn default() -> Self {
Self::new()
}
}
impl Drop for RawCondvar {
fn drop(&mut self) {
unsafe {
pthread_cond_destroy(self.0.get());
}
}
}
fn monotonic_deadline(timeout: Duration) -> timespec {
let mut now = timespec::default();
unsafe {
clock_gettime(CLOCK_MONOTONIC, &mut now);
}
let mut tv_sec = now.tv_sec + timeout.as_secs() as c_long;
let mut tv_nsec = now.tv_nsec + timeout.subsec_nanos() as c_long;
if tv_nsec >= 1_000_000_000 {
tv_sec += 1;
tv_nsec -= 1_000_000_000;
}
timespec { tv_sec, tv_nsec }
}
#[derive(Default)]
struct NotifyState {
value: u32,
pending: bool,
}
struct NotifySlot {
state: Mutex<NotifyState>,
cv: RawCondvar,
}
impl Default for NotifySlot {
fn default() -> Self {
Self {
state: Mutex::new(NotifyState::default()),
cv: RawCondvar::default(),
}
}
}
fn notify_registry() -> &'static Mutex<HashMap<ThreadHandle, Arc<NotifySlot>>> {
static mut ONCE: pthread_once_t = PTHREAD_ONCE_INIT;
static mut REGISTRY: *mut Mutex<HashMap<ThreadHandle, Arc<NotifySlot>>> = null_mut();
extern "C" fn init() {
unsafe {
REGISTRY = Box::into_raw(Box::new(Mutex::new(HashMap::new())));
}
}
unsafe {
pthread_once(&raw mut ONCE, Some(init));
&*REGISTRY
}
}
fn notify_slot(handle: ThreadHandle) -> Arc<NotifySlot> {
notify_registry()
.lock()
.unwrap()
.entry(handle)
.or_insert_with(|| Arc::new(NotifySlot::default()))
.clone()
}
fn forget_notify_slot(handle: ThreadHandle) {
if let Ok(mut registry) = notify_registry().lock() {
registry.remove(&handle);
}
}
fn thread_registry() -> &'static Mutex<HashMap<ThreadHandle, ThreadMetadata>> {
static mut ONCE: pthread_once_t = PTHREAD_ONCE_INIT;
static mut REGISTRY: *mut Mutex<HashMap<ThreadHandle, ThreadMetadata>> = null_mut();
extern "C" fn init() {
unsafe {
REGISTRY = Box::into_raw(Box::new(Mutex::new(HashMap::new())));
}
}
unsafe {
pthread_once(&raw mut ONCE, Some(init));
&*REGISTRY
}
}
fn register_thread(metadata: ThreadMetadata) {
if let Ok(mut registry) = thread_registry().lock() {
registry.insert(metadata.thread, metadata);
}
}
fn forget_thread(handle: ThreadHandle) {
if let Ok(mut registry) = thread_registry().lock() {
registry.remove(&handle);
}
}
fn set_thread_state(handle: ThreadHandle, state: ThreadState) {
if let Ok(mut registry) = thread_registry().lock() {
if let Some(metadata) = registry.get_mut(&handle) {
metadata.state = state;
}
}
}
fn registered_thread_metadata(handle: ThreadHandle) -> Option<ThreadMetadata> {
thread_registry().lock().ok().and_then(|registry| registry.get(&handle).cloned())
}
fn effective_thread_state(handle: ThreadHandle, tracked: ThreadState) -> ThreadState {
if handle == unsafe { pthread_self() } { ThreadState::Running } else { tracked }
}
pub(crate) fn all_registered_threads() -> Vec<ThreadMetadata> {
thread_registry()
.lock()
.map(|registry| registry.values().cloned().collect())
.unwrap_or_default()
}
pub(crate) fn registered_thread_count() -> usize {
thread_registry().lock().map(|registry| registry.len()).unwrap_or(0)
}
fn apply_notification(state: &mut NotifyState, notification: ThreadNotification) -> Result<()> {
use ThreadNotification::*;
match notification {
NoAction => {}
SetBits(bits) => state.value |= bits,
Increment => state.value = state.value.wrapping_add(1),
SetValueWithOverwrite(value) => state.value = value,
SetValueWithoutOverwrite(value) => {
if state.pending {
return Err(Error::QueueFull);
}
state.value = value;
}
}
state.pending = true;
Ok(())
}
#[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>,
}
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: 0,
name: Bytes::from_str(name),
stack_depth,
priority,
callback: None,
param: None,
}
}
pub fn new_with_handle(handle: ThreadHandle, name: &str, stack_depth: StackType, priority: UBaseType) -> Result<Self> {
if handle == 0 {
return Err(Error::NullPtr);
}
Ok(Self {
handle,
name: Bytes::from_str(name),
stack_depth,
priority,
callback: None,
param: None,
})
}
#[inline]
pub fn new_with_to_priority(name: &str, stack_depth: StackType, priority: impl ToPriority) -> Self {
Self::new(name, stack_depth, priority.to_priority())
}
#[inline]
pub fn new_with_handle_and_to_priority(handle: ThreadHandle, name: &str, stack_depth: StackType, priority: impl ToPriority) -> Result<Self> {
Self::new_with_handle(handle, name, stack_depth, priority.to_priority())
}
pub fn get_metadata_from_handle(handle: ThreadHandle) -> ThreadMetadata {
if handle == 0 {
return ThreadMetadata::default();
}
match registered_thread_metadata(handle) {
Some(metadata) => ThreadMetadata {
state: effective_thread_state(handle, metadata.state),
..metadata
},
None => ThreadMetadata {
thread: handle,
name: Bytes::from_str("thread"),
stack_depth: 0,
priority: 0,
thread_number: 0,
state: effective_thread_state(handle, ThreadState::Ready),
current_priority: 0,
base_priority: 0,
run_time_counter: 0,
stack_high_water_mark: 0,
},
}
}
pub fn get_metadata(thread: &Thread) -> ThreadMetadata {
let state = if thread.is_null() {
ThreadState::Invalid
} else {
let tracked = registered_thread_metadata(thread.handle).map(|metadata| metadata.state).unwrap_or(ThreadState::Ready);
effective_thread_state(thread.handle, tracked)
};
ThreadMetadata {
thread: thread.handle,
name: thread.name.clone(),
stack_depth: thread.stack_depth,
priority: thread.priority,
thread_number: thread.handle,
state,
current_priority: thread.priority,
base_priority: thread.priority,
run_time_counter: 0,
stack_high_water_mark: 0,
}
}
#[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> {
self.wait_notification(bits_to_clear_on_entry, bits_to_clear_on_exit, timeout_ticks.to_ticks())
}
fn metadata(&self) -> ThreadMetadata {
Self::get_metadata(self)
}
}
unsafe extern "C" fn callback_c_wrapper(param_ptr: *mut c_void) -> *mut c_void {
if param_ptr.is_null() {
return null_mut();
}
let mut thread_instance: Box<Thread> = unsafe { Box::from_raw(param_ptr as *mut _) };
thread_instance.as_mut().handle = unsafe { pthread_self() };
let handle = thread_instance.handle;
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)
};
set_thread_state(handle, ThreadState::Deleted);
Box::into_raw(Box::new(ret)) as *mut c_void
}
unsafe extern "C" fn simple_callback_c_wrapper(param_ptr: *mut c_void) -> *mut c_void {
if param_ptr.is_null() {
return null_mut();
}
let func: Box<Arc<ThreadSimpleFnPtr>> = unsafe { Box::from_raw(param_ptr as *mut _) };
let ret = func();
set_thread_state(unsafe { pthread_self() }, ThreadState::Deleted);
Box::into_raw(Box::new(ret)) as *mut c_void
}
impl ThreadFn for Thread {
fn is_null(&self) -> bool {
self.handle == 0
}
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,
Self: Sized,
{
let func: Arc<ThreadFnPtr> = Arc::new(callback);
self.callback = Some(func);
self.param = param.clone();
let mut attr: pthread_attr_t = Default::default();
unsafe {
pthread_attr_init (&mut attr);
}
let requested_stack_size = PTHREAD_STACK_MIN + self.stack_depth as usize;
let min_safe_stack_size = 1024usize * 1024usize;
unsafe {
pthread_attr_setstacksize (&mut attr, if requested_stack_size < min_safe_stack_size { min_safe_stack_size } else { requested_stack_size });
}
#[cfg(feature = "real_time")]
unsafe {
let fifo_param = sched_param {
sched_priority: self.priority as core::ffi::c_int,
};
pthread_attr_setinheritsched(&mut attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&mut attr, SCHED_FIFO);
pthread_attr_setschedparam(&mut attr, &fifo_param);
}
let boxed_thread = Box::new(self.clone());
let ret = unsafe {
pthread_create(&mut self.handle, &attr, Some(callback_c_wrapper), Box::into_raw(boxed_thread) as *mut c_void)
};
if ret != 0 {
return Err(Error::ReturnWithCode(ret));
}
unsafe {
pthread_setname_np(self.handle, self.name.as_cstr().as_ptr());
}
register_thread(ThreadMetadata {
thread: self.handle,
name: self.name.clone(),
stack_depth: self.stack_depth,
priority: self.priority,
thread_number: 0,
state: ThreadState::Ready,
current_priority: self.priority,
base_priority: self.priority,
run_time_counter: 0,
stack_high_water_mark: 0,
});
Ok(Self {
handle: self.handle,
name: self.name.clone(),
stack_depth: self.stack_depth,
priority: self.priority,
callback: self.callback.clone(),
param,
})
}
fn spawn_simple<F>(&mut self, callback: F) -> Result<Self>
where
F: Fn() -> Result<ThreadParam> + Send + Sync + 'static,
Self: Sized,
{
let func: Arc<ThreadSimpleFnPtr> = Arc::new(callback);
let boxed_func = Box::new(func);
let mut attr: pthread_attr_t = Default::default();
unsafe {
pthread_attr_init (&mut attr);
}
let requested_stack_size = PTHREAD_STACK_MIN + self.stack_depth as usize;
let min_safe_stack_size = 1024usize * 1024usize;
unsafe {
pthread_attr_setstacksize (&mut attr, if requested_stack_size < min_safe_stack_size { min_safe_stack_size } else { requested_stack_size });
}
#[cfg(feature = "real_time")]
unsafe {
let fifo_param = sched_param {
sched_priority: self.priority as core::ffi::c_int,
};
pthread_attr_setinheritsched(&mut attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&mut attr, SCHED_FIFO);
pthread_attr_setschedparam(&mut attr, &fifo_param);
}
let ret = unsafe {
pthread_create(&mut self.handle, &attr, Some(simple_callback_c_wrapper), Box::into_raw(boxed_func) as *mut c_void)
};
if ret != 0 {
return Err(Error::ReturnWithCode(ret));
}
unsafe {
pthread_setname_np(self.handle, self.name.as_cstr().as_ptr());
}
register_thread(ThreadMetadata {
thread: self.handle,
name: self.name.clone(),
stack_depth: self.stack_depth,
priority: self.priority,
thread_number: 0,
state: ThreadState::Ready,
current_priority: self.priority,
base_priority: self.priority,
run_time_counter: 0,
stack_high_water_mark: 0,
});
Ok(Self {
handle: self.handle,
name: self.name.clone(),
stack_depth: self.stack_depth,
priority: self.priority,
callback: self.callback.clone(),
param: self.param.clone(),
})
}
fn delete(&self) {
let _ = unsafe { pthread_join(self.handle, null_mut()) };
forget_notify_slot(self.handle);
forget_thread(self.handle);
}
fn suspend(&self) {
if self.is_null() {
return;
}
ensure_suspend_signal_handlers();
unsafe {
pthread_kill(self.handle, suspend_signal());
}
set_thread_state(self.handle, ThreadState::Suspended);
}
fn resume(&self) {
if self.is_null() {
return;
}
ensure_suspend_signal_handlers();
unsafe {
pthread_kill(self.handle, resume_signal());
}
set_thread_state(self.handle, ThreadState::Ready);
}
fn join(&self, ret_val: DoublePtr) -> Result<i32> {
if self.is_null() {
return Err(Error::NullPtr);
}
let ret = unsafe { pthread_join(self.handle, ret_val) };
if ret != 0 {
Err(Error::ReturnWithCode(ret))
} else {
forget_notify_slot(self.handle);
forget_thread(self.handle);
Ok(0)
}
}
fn get_metadata(&self) -> ThreadMetadata {
self.metadata()
}
fn get_current() -> Self
where
Self: Sized,
{
Self {
handle: unsafe { pthread_self() },
name: Bytes::from_str("current"),
stack_depth: 0,
priority: 0,
callback: None,
param: None,
}
}
fn notify(&self, notification: ThreadNotification) -> Result<()> {
if self.is_null() {
return Err(Error::NullPtr);
}
let slot = notify_slot(self.handle);
let result = {
let mut state = slot.state.lock().unwrap();
apply_notification(&mut state, notification)
};
if result.is_ok() {
slot.cv.notify_all();
}
result
}
fn notify_from_isr(&self, notification: ThreadNotification, higher_priority_task_woken: &mut BaseType) -> Result<()> {
*higher_priority_task_woken = 0;
self.notify(notification)
}
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 slot = notify_slot(self.handle);
let mut state = slot.state.lock().unwrap();
state.value &= !bits_to_clear_on_entry;
if !state.pending {
set_thread_state(self.handle, ThreadState::Blocked);
if timeout_ticks == TickType::MAX {
loop {
if state.pending {
break;
}
slot.cv.wait(&state);
}
} else {
let deadline = monotonic_deadline(Duration::from_millis((timeout_ticks as u64).saturating_mul(TICK_PERIOD_MS)));
loop {
if state.pending {
break;
}
if slot.cv.wait_until(&state, deadline) {
break;
}
}
}
set_thread_state(self.handle, ThreadState::Ready);
}
if !state.pending {
return Err(Error::Timeout);
}
state.pending = false;
let value = state.value;
state.value &= !bits_to_clear_on_exit;
Ok(value)
}
}
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
)
}
}