use core::{fmt, marker::PhantomData};
use r3_core::{
closure::Closure,
kernel::{traits, SetTimerDelayError, SetTimerPeriodError, StartTimerError, StopTimerError},
time::Duration,
utils::Init,
};
use crate::{
error::NoAccessError,
klock::{assume_cpu_lock, lock_cpu, CpuLockCell, CpuLockGuard, CpuLockTokenRefMut},
timeout,
utils::pin::static_pin,
Id, KernelCfg2, KernelTraits, System,
};
pub(super) type TimerId = Id;
impl<Traits: KernelTraits> System<Traits> {
#[inline]
unsafe fn timer_cb(this: TimerId) -> Result<&'static TimerCb<Traits>, NoAccessError> {
Traits::get_timer_cb(this.get() - 1).ok_or_else(|| unsafe { crate::bad_id::<Traits>() })
}
}
unsafe impl<Traits: KernelTraits> traits::KernelTimer for System<Traits> {
type RawTimerId = TimerId;
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
unsafe fn raw_timer_start(this: TimerId) -> Result<(), StartTimerError> {
let mut lock = lock_cpu::<Traits>()?;
let timer_cb = unsafe { Self::timer_cb(this)? };
start_timer(lock.borrow_mut(), timer_cb);
Ok(())
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
unsafe fn raw_timer_stop(this: TimerId) -> Result<(), StopTimerError> {
let mut lock = lock_cpu::<Traits>()?;
let timer_cb = unsafe { Self::timer_cb(this)? };
stop_timer(lock.borrow_mut(), timer_cb);
Ok(())
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
unsafe fn raw_timer_set_delay(
this: TimerId,
delay: Option<Duration>,
) -> Result<(), SetTimerDelayError> {
let time32 = if let Some(x) = delay {
timeout::time32_from_duration(x)?
} else {
timeout::BAD_DURATION32
};
let mut lock = lock_cpu::<Traits>()?;
let timer_cb = unsafe { Self::timer_cb(this)? };
set_timer_delay(lock.borrow_mut(), timer_cb, time32);
Ok(())
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
unsafe fn raw_timer_set_period(
this: TimerId,
period: Option<Duration>,
) -> Result<(), SetTimerPeriodError> {
let time32 = if let Some(x) = period {
timeout::time32_from_duration(x)?
} else {
timeout::BAD_DURATION32
};
let mut lock = lock_cpu::<Traits>()?;
let timer_cb = unsafe { Self::timer_cb(this)? };
set_timer_period(lock.borrow_mut(), timer_cb, time32);
Ok(())
}
}
#[doc(hidden)]
pub struct TimerCb<Traits: KernelCfg2> {
pub(super) attr: &'static TimerAttr<Traits>,
pub(super) timeout: timeout::Timeout<Traits>,
pub(super) active: CpuLockCell<Traits, bool>,
pub(super) period: CpuLockCell<Traits, timeout::Time32>,
}
impl<Traits: KernelTraits> Init for TimerCb<Traits> {
#[allow(clippy::declare_interior_mutable_const)]
const INIT: Self = Self {
attr: &Init::INIT,
timeout: Init::INIT,
active: Init::INIT,
period: Init::INIT,
};
}
impl<Traits: KernelTraits> fmt::Debug for TimerCb<Traits> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TimerCb")
.field("self", &(self as *const _))
.field("attr", &self.attr)
.field("timeout", &self.timeout)
.field("active", &self.active)
.field("period", &self.period)
.finish()
}
}
#[doc(hidden)]
pub struct TimerAttr<Traits> {
pub(super) entry_point: Closure,
pub(super) init_active: bool,
pub(super) _phantom: PhantomData<Traits>,
}
impl<Traits> Init for TimerAttr<Traits> {
const INIT: Self = Self {
entry_point: Closure::INIT,
init_active: false,
_phantom: PhantomData,
};
}
impl<Traits: KernelTraits> fmt::Debug for TimerAttr<Traits> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TimerAttr")
.field("entry_point", &self.entry_point)
.finish()
}
}
#[inline]
pub(super) fn init_timer<Traits: KernelTraits>(
mut lock: CpuLockTokenRefMut<'_, Traits>,
timer_cb: &'static TimerCb<Traits>,
) {
if timer_cb.attr.init_active {
let delay = timer_cb.timeout.at_raw(lock.borrow_mut());
if delay != timeout::BAD_DURATION32 {
timeout::insert_timeout(lock.borrow_mut(), static_pin(&timer_cb.timeout));
}
timer_cb.active.replace(&mut *lock, true);
}
}
#[inline]
fn start_timer<Traits: KernelTraits>(
mut lock: CpuLockTokenRefMut<'_, Traits>,
timer_cb: &'static TimerCb<Traits>,
) {
if timer_cb.active.get(&*lock) {
return;
}
let delay = timer_cb.timeout.at_raw(lock.borrow_mut());
if delay != timeout::BAD_DURATION32 {
timer_cb
.timeout
.set_expiration_after(lock.borrow_mut(), delay);
timeout::insert_timeout(lock.borrow_mut(), static_pin(&timer_cb.timeout));
}
timer_cb.active.replace(&mut *lock, true);
}
#[inline]
fn stop_timer<Traits: KernelTraits>(
mut lock: CpuLockTokenRefMut<'_, Traits>,
timer_cb: &TimerCb<Traits>,
) {
if timer_cb.timeout.is_linked(lock.borrow_mut()) {
debug_assert!(timer_cb.active.get(&*lock));
let delay = timer_cb
.timeout
.saturating_duration_until_timeout(lock.borrow_mut());
timeout::remove_timeout(lock.borrow_mut(), &timer_cb.timeout);
timer_cb.timeout.set_at_raw(lock.borrow_mut(), delay);
}
timer_cb.active.replace(&mut *lock, false);
}
#[inline]
fn set_timer_delay<Traits: KernelTraits>(
mut lock: CpuLockTokenRefMut<'_, Traits>,
timer_cb: &'static TimerCb<Traits>,
delay: timeout::Time32,
) {
let is_active = timer_cb.active.get(&*lock);
if timer_cb.timeout.is_linked(lock.borrow_mut()) {
timeout::remove_timeout(lock.borrow_mut(), &timer_cb.timeout);
}
if is_active && delay != timeout::BAD_DURATION32 {
timer_cb
.timeout
.set_expiration_after(lock.borrow_mut(), delay);
timeout::insert_timeout(lock.borrow_mut(), static_pin(&timer_cb.timeout));
} else {
timer_cb.timeout.set_at_raw(lock.borrow_mut(), delay);
}
}
#[inline]
fn set_timer_period<Traits: KernelTraits>(
mut lock: CpuLockTokenRefMut<'_, Traits>,
timer: &TimerCb<Traits>,
period: timeout::Time32,
) {
timer.period.replace(&mut *lock, period);
}
pub(super) fn timer_timeout_handler<Traits: KernelTraits>(
i: usize,
mut lock: CpuLockGuard<Traits>,
) -> CpuLockGuard<Traits> {
let timer_cb = Traits::get_timer_cb(i).unwrap();
debug_assert!(!timer_cb.timeout.is_linked(lock.borrow_mut()));
debug_assert!(timer_cb.active.get(&*lock));
let period = timer_cb.period.get(&*lock);
if period == timeout::BAD_DURATION32 {
timer_cb
.timeout
.set_at_raw(lock.borrow_mut(), timeout::BAD_DURATION32);
} else {
timer_cb
.timeout
.adjust_expiration(lock.borrow_mut(), period);
timeout::insert_timeout(lock.borrow_mut(), static_pin(&timer_cb.timeout));
}
drop(lock);
timer_cb.attr.entry_point.call();
lock_cpu().unwrap_or_else(|_| unsafe { assume_cpu_lock() })
}