use core::ffi::c_long;
use core::fmt::{Debug, Display};
use core::ops::Deref;
use core::ptr::null_mut;
use core::time::Duration;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use alloc::boxed::Box;
use alloc::sync::Arc;
use crate::os::ThreadFn;
use crate::posix::config::TICK_PERIOD_MS;
use crate::posix::ffi::{CLOCK_MONOTONIC, clock_gettime, monotonic_deadline, pthread_self, timespec};
use crate::posix::mutex::Mutex;
use crate::posix::thread::{RawCondvar, Thread};
use crate::posix::types::{StackType, TickType, TimerHandle, UBaseType};
use crate::traits::{MAX_TASK_NAME_LEN, MutexFn, TimerFn, TimerFnPtr, TimerParam, ToTick};
use crate::utils::{Bytes, OsalRsBool, Result};
const TIMER_THREAD_NAME: &str = "os_timer";
const TIMER_THREAD_STACK: StackType = 1024;
const NSECS_PER_SEC: c_long = 1_000_000_000;
fn timer_thread_priority() -> UBaseType {
#[cfg(feature = "real_time")]
{
use crate::posix::ffi::{SCHED_FIFO, sched_get_priority_min};
(unsafe { sched_get_priority_min(SCHED_FIFO) }).max(0) as UBaseType
}
#[cfg(not(feature = "real_time"))]
{
1
}
}
#[derive(Default)]
struct TimerState {
deadline: Option<timespec>,
exit: bool,
}
struct TimerShared {
state: Mutex<TimerState>,
cv: RawCondvar,
ready: AtomicBool,
us: AtomicU32,
oneshot: AtomicBool,
thread: Mutex<Option<Thread>>,
}
impl TimerShared {
fn destroy(&self) {
self.ready.store(false, Ordering::Release);
if let Ok(mut state) = self.state.lock() {
state.exit = true;
state.deadline = None;
}
self.cv.notify_all();
let Ok(mut guard) = self.thread.lock() else {
return;
};
let Some(bg_thread) = guard.take() else {
return;
};
if *bg_thread == unsafe { pthread_self() } {
bg_thread.detach();
return;
}
bg_thread.delete();
}
}
struct TimerOwner(Arc<TimerShared>);
impl Drop for TimerOwner {
fn drop(&mut self) {
self.0.destroy();
}
}
#[derive(Clone)]
pub struct Timer {
pub handle: TimerHandle,
name: Bytes<MAX_TASK_NAME_LEN>,
callback: Option<Arc<TimerFnPtr>>,
param: Option<TimerParam>,
owner: Option<Arc<TimerOwner>>,
shared: Option<Arc<TimerShared>>,
}
unsafe impl Send for Timer {}
unsafe impl Sync for Timer {}
fn ticks_to_us(ticks: TickType) -> u32 {
(ticks as u64).saturating_mul(TICK_PERIOD_MS).saturating_mul(1000).min(u32::MAX as u64) as u32
}
fn monotonic_now() -> timespec {
let mut now = timespec::default();
unsafe {
clock_gettime(CLOCK_MONOTONIC, &mut now);
}
now
}
fn is_due(deadline: ×pec, now: ×pec) -> bool {
(deadline.tv_sec, deadline.tv_nsec) <= (now.tv_sec, now.tv_nsec)
}
fn add_us(time: ×pec, us: u32) -> timespec {
let nanoseconds = (us as c_long % 1_000_000) * 1000 + time.tv_nsec;
timespec {
tv_sec: time.tv_sec + us as c_long / 1_000_000 + nanoseconds / NSECS_PER_SEC,
tv_nsec: nanoseconds % NSECS_PER_SEC,
}
}
fn arm(shared: &TimerShared, us: u32) -> OsalRsBool {
if !shared.ready.load(Ordering::Acquire) {
return OsalRsBool::False;
}
let Ok(mut state) = shared.state.lock() else {
return OsalRsBool::False;
};
state.deadline = if us == 0 {
None
} else {
Some(monotonic_deadline(Duration::from_micros(us as u64)))
};
drop(state);
shared.cv.notify_all();
OsalRsBool::True
}
fn run_timer_thread(shared: Arc<TimerShared>, name: Bytes<MAX_TASK_NAME_LEN>, callback: Option<Arc<TimerFnPtr>>, mut param: Option<TimerParam>) -> Result<TimerParam> {
loop {
let Ok(mut state) = shared.state.lock() else {
break;
};
loop {
if state.exit {
break;
}
match state.deadline {
None => shared.cv.wait(&state),
Some(deadline) if is_due(&deadline, &monotonic_now()) => break,
Some(deadline) => {
shared.cv.wait_until(&state, deadline);
}
}
}
if state.exit {
break;
}
state.deadline = if shared.oneshot.load(Ordering::Acquire) {
None
} else {
let us = shared.us.load(Ordering::Acquire);
let now = monotonic_now();
let next = state.deadline.map(|deadline| add_us(&deadline, us)).unwrap_or(now);
Some(if is_due(&next, &now) { add_us(&now, us) } else { next })
};
drop(state);
if let Some(cb) = &callback {
let timer_self = Timer {
handle: Arc::as_ptr(&shared) as TimerHandle,
name,
callback: callback.clone(),
param: param.clone(),
owner: None,
shared: Some(shared.clone()),
};
if let Ok(new_param) = cb(Box::new(timer_self), param.clone()) {
param = Some(new_param);
}
}
}
let final_param: TimerParam = match param {
Some(p) => p,
None => Arc::new(()),
};
Ok(final_param)
}
impl Timer {
#[inline]
pub fn new_with_to_tick<F>(name: &str, timer_period_in_ticks: impl ToTick, auto_reload: bool, param: Option<TimerParam>, callback: F) -> Result<Self>
where
F: Fn(Box<dyn TimerFn>, Option<TimerParam>) -> Result<TimerParam> + Send + Sync + Clone + 'static,
{
Self::new(name, timer_period_in_ticks.to_ticks(), auto_reload, param, callback)
}
#[inline]
pub fn start_with_to_tick(&self, ticks_to_wait: impl ToTick) -> OsalRsBool {
self.start(ticks_to_wait.to_ticks())
}
#[inline]
pub fn stop_with_to_tick(&self, ticks_to_wait: impl ToTick) -> OsalRsBool {
self.stop(ticks_to_wait.to_ticks())
}
#[inline]
pub fn reset_with_to_tick(&self, ticks_to_wait: impl ToTick) -> OsalRsBool {
self.reset(ticks_to_wait.to_ticks())
}
#[inline]
pub fn change_period_with_to_tick(&self, new_period_in_ticks: impl ToTick, new_period_ticks: impl ToTick) -> OsalRsBool {
self.change_period(new_period_in_ticks.to_ticks(), new_period_ticks.to_ticks())
}
#[inline]
pub fn delete_with_to_tick(&mut self, ticks_to_wait: impl ToTick) -> OsalRsBool {
self.delete(ticks_to_wait.to_ticks())
}
pub fn new<F>(name: &str, timer_period_in_ticks: TickType, auto_reload: bool, param: Option<TimerParam>, callback: F) -> Result<Self>
where
F: Fn(Box<dyn TimerFn>, Option<TimerParam>) -> Result<TimerParam> + Send + Sync + Clone + 'static,
{
let shared = Arc::new(TimerShared {
state: Mutex::new(TimerState::default()),
cv: RawCondvar::new(),
ready: AtomicBool::new(false),
us: AtomicU32::new(ticks_to_us(timer_period_in_ticks)),
oneshot: AtomicBool::new(!auto_reload),
thread: Mutex::new(None),
});
let name = Bytes::<MAX_TASK_NAME_LEN>::from_str(name);
let mut timer = Self {
handle: Arc::as_ptr(&shared) as TimerHandle,
name,
callback: Some(Arc::new(callback)),
param,
owner: Some(Arc::new(TimerOwner(shared.clone()))),
shared: Some(shared.clone()),
};
let bg_shared = shared.clone();
let bg_name = name;
let bg_callback = timer.callback.clone();
let bg_param = timer.param.clone();
let mut bg_thread = Thread::new(TIMER_THREAD_NAME, TIMER_THREAD_STACK, timer_thread_priority());
let bg_thread = match bg_thread.spawn_simple(move || run_timer_thread(bg_shared.clone(), bg_name, bg_callback.clone(), bg_param.clone())) {
Ok(thread) => thread,
Err(err) => {
timer.handle = null_mut();
return Err(err);
}
};
*shared.thread.lock().unwrap() = Some(bg_thread);
shared.ready.store(true, Ordering::Release);
Ok(timer)
}
}
impl TimerFn for Timer {
fn is_null(&self) -> bool {
match &self.shared {
Some(shared) => !shared.ready.load(Ordering::Acquire),
None => true,
}
}
fn start(&self, _ticks_to_wait: TickType) -> OsalRsBool {
let Some(shared) = &self.shared else {
return OsalRsBool::False;
};
arm(shared, shared.us.load(Ordering::Acquire))
}
fn stop(&self, _ticks_to_wait: TickType) -> OsalRsBool {
let Some(shared) = &self.shared else {
return OsalRsBool::False;
};
arm(shared, 0)
}
fn reset(&self, ticks_to_wait: TickType) -> OsalRsBool {
self.start(ticks_to_wait)
}
fn change_period(&self, new_period_in_ticks: TickType, ticks_to_wait: TickType) -> OsalRsBool {
let Some(shared) = &self.shared else {
return OsalRsBool::False;
};
shared.us.store(ticks_to_us(new_period_in_ticks), Ordering::Release);
self.start(ticks_to_wait)
}
fn delete(&mut self, _ticks_to_wait: TickType) -> OsalRsBool {
let Some(shared) = self.shared.take() else {
return OsalRsBool::False;
};
shared.destroy();
self.owner = None;
self.handle = null_mut();
OsalRsBool::True
}
}
impl Deref for Timer {
type Target = TimerHandle;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl Debug for Timer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Timer")
.field("handle", &self.handle)
.field("name", &self.name)
.field("has_callback", &self.callback.is_some())
.field("has_param", &self.param.is_some())
.field("is_null", &self.is_null())
.finish()
}
}
impl Display for Timer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Timer {{ name: {}, handle: {:?} }}", self.name, self.handle)
}
}