asterinas/framework/aster-frame/src/timer.rs

106 lines
3.2 KiB
Rust
Raw Normal View History

2024-01-03 03:22:36 +00:00
// SPDX-License-Identifier: MPL-2.0
2022-08-08 01:01:42 +00:00
//! Timer.
2023-05-31 02:46:51 +00:00
use core::{sync::atomic::Ordering, time::Duration};
2022-08-08 01:01:42 +00:00
2024-02-05 06:01:18 +00:00
pub use crate::arch::timer::read_monotonic_milli_seconds;
use crate::{
arch::timer::{add_timeout_list, TimerCallback, TICK, TIMER_FREQ},
prelude::*,
sync::SpinLock,
};
2023-05-31 02:46:51 +00:00
2022-08-08 01:01:42 +00:00
/// A timer invokes a callback function after a specified span of time elapsed.
///
/// A new timer is initially inactive. Only after a timeout value is set with
/// the `set` method can the timer become active and the callback function
/// be triggered.
///
/// Timers are one-shot. If the time is out, one has to set the timer again
/// in order to trigger the callback again.
2022-11-09 12:33:41 +00:00
pub struct Timer {
2022-11-11 02:14:42 +00:00
function: Arc<dyn Fn(Arc<Self>) + Send + Sync>,
2023-09-21 02:16:33 +00:00
inner: SpinLock<TimerInner>,
2022-11-09 12:33:41 +00:00
}
#[derive(Default)]
2022-11-11 02:14:42 +00:00
struct TimerInner {
2022-11-09 12:33:41 +00:00
start_tick: u64,
2022-11-11 02:14:42 +00:00
timeout_tick: u64,
timer_callback: Option<Arc<TimerCallback>>,
2022-11-09 12:33:41 +00:00
}
2022-11-11 02:14:42 +00:00
fn timer_callback(callback: &TimerCallback) {
2022-11-09 12:33:41 +00:00
let data = callback.data();
2022-11-11 02:14:42 +00:00
if data.is::<Arc<Timer>>() {
2022-11-09 12:33:41 +00:00
let timer = data.downcast_ref::<Arc<Timer>>().unwrap();
timer.function.call((timer.clone(),));
2022-11-11 02:14:42 +00:00
} else {
2022-11-09 12:33:41 +00:00
panic!("the timer callback is not Timer structure");
}
}
2022-11-11 02:14:42 +00:00
const NANOS_DIVIDE: u64 = 1_000_000_000 / TIMER_FREQ;
2022-08-08 01:01:42 +00:00
impl Timer {
/// Creates a new instance, given a callback function.
2022-11-09 12:33:41 +00:00
pub fn new<F>(f: F) -> Result<Arc<Self>>
2022-08-08 01:01:42 +00:00
where
2022-11-11 02:14:42 +00:00
F: Fn(Arc<Timer>) + Send + Sync + 'static,
2022-08-08 01:01:42 +00:00
{
2022-11-11 02:14:42 +00:00
Ok(Arc::new(Self {
function: Arc::new(f),
2023-09-21 02:16:33 +00:00
inner: SpinLock::new(TimerInner::default()),
2022-11-11 02:14:42 +00:00
}))
2022-08-08 01:01:42 +00:00
}
/// Set a timeout value.
///
/// If a timeout value is already set, the timeout value will be refreshed.
2022-11-11 02:14:42 +00:00
///
2023-12-12 07:00:40 +00:00
pub fn set(self: &Arc<Self>, timeout: Duration) {
2023-09-21 02:16:33 +00:00
let mut lock = self.inner.lock_irq_disabled();
2022-11-11 02:14:42 +00:00
match &lock.timer_callback {
2022-11-09 12:33:41 +00:00
Some(callback) => {
2023-10-08 09:40:58 +00:00
callback.cancel();
2022-11-11 02:14:42 +00:00
}
None => {}
2022-11-09 12:33:41 +00:00
}
let tick_count =
timeout.as_secs() * TIMER_FREQ + timeout.subsec_nanos() as u64 / NANOS_DIVIDE;
2023-05-31 02:46:51 +00:00
let tick = TICK.load(Ordering::SeqCst);
lock.start_tick = tick;
lock.timeout_tick = tick + tick_count;
lock.timer_callback = Some(add_timeout_list(tick_count, self.clone(), timer_callback));
2022-08-08 01:01:42 +00:00
}
/// Returns the remaining timeout value.
///
/// If the timer is not set, then the remaining timeout value is zero.
pub fn remain(&self) -> Duration {
2023-09-21 02:16:33 +00:00
let lock = self.inner.lock_irq_disabled();
2023-05-31 02:46:51 +00:00
let tick_remain = {
let tick = TICK.load(Ordering::SeqCst) as i64;
lock.timeout_tick as i64 - tick
};
2022-11-11 02:14:42 +00:00
if tick_remain <= 0 {
Duration::new(0, 0)
} else {
let second_count = tick_remain as u64 / TIMER_FREQ;
2022-11-09 12:33:41 +00:00
let remain_count = tick_remain as u64 % TIMER_FREQ;
2022-11-11 02:14:42 +00:00
Duration::new(second_count, (remain_count * NANOS_DIVIDE) as u32)
2022-11-09 12:33:41 +00:00
}
2022-08-08 01:01:42 +00:00
}
/// Clear the timeout value.
pub fn clear(&self) {
2023-09-21 02:16:33 +00:00
let mut lock = self.inner.lock_irq_disabled();
2022-11-11 02:14:42 +00:00
if let Some(callback) = &lock.timer_callback {
2023-10-08 09:40:58 +00:00
callback.cancel();
2022-11-09 12:33:41 +00:00
}
lock.timeout_tick = 0;
lock.start_tick = 0;
lock.timer_callback = None;
2022-08-08 01:01:42 +00:00
}
}