blob: 946c6e5f21704def61d7944597307d2ca9b04205 [file] [log] [blame]
//! Async timer driver. Can be used for (non-busy) sleeping.
use crate::callback::CallbackSubscription;
use crate::callback::Consumer;
use crate::futures;
use crate::result::OtherError;
use crate::result::TockError;
use crate::result::TockResult;
use crate::result::EALREADY;
use crate::syscalls;
use core::cell::Cell;
use core::isize;
use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Sub};
const DRIVER_NUMBER: usize = 0x00000;
mod command_nr {
pub const IS_DRIVER_AVAILABLE: usize = 0;
pub const GET_CLOCK_FREQUENCY: usize = 1;
pub const GET_CLOCK_VALUE: usize = 2;
pub const STOP_ALARM: usize = 3;
pub const SET_ALARM: usize = 4;
}
mod subscribe_nr {
pub const SUBSCRIBE_CALLBACK: usize = 0;
}
pub struct WithCallback<'a, CB> {
callback: CB,
clock_frequency: ClockFrequency,
phantom: PhantomData<&'a mut ()>,
}
struct TimerEventConsumer;
impl<CB: FnMut(ClockValue, Alarm)> Consumer<WithCallback<'_, CB>> for TimerEventConsumer {
fn consume(data: &mut WithCallback<CB>, clock_value: usize, alarm_id: usize, _: usize) {
(data.callback)(
ClockValue {
num_ticks: clock_value as isize,
clock_frequency: data.clock_frequency,
},
Alarm { alarm_id },
);
}
}
impl<'a, CB: FnMut(ClockValue, Alarm)> WithCallback<'a, CB> {
pub fn init(&'a mut self) -> TockResult<Timer<'a>> {
let num_notifications =
syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?;
let clock_frequency =
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)?;
if clock_frequency == 0 {
return Err(OtherError::TimerDriverErroneousClockFrequency.into());
}
let clock_frequency = ClockFrequency {
hz: clock_frequency,
};
let subscription = syscalls::subscribe::<TimerEventConsumer, _>(
DRIVER_NUMBER,
subscribe_nr::SUBSCRIBE_CALLBACK,
self,
)?;
Ok(Timer {
num_notifications,
clock_frequency,
subscription,
})
}
}
pub struct Timer<'a> {
num_notifications: usize,
clock_frequency: ClockFrequency,
#[allow(dead_code)] // Used in drop
subscription: CallbackSubscription<'a>,
}
impl<'a> Timer<'a> {
pub fn num_notifications(&self) -> usize {
self.num_notifications
}
pub fn clock_frequency(&self) -> ClockFrequency {
self.clock_frequency
}
pub fn get_current_clock(&self) -> TockResult<ClockValue> {
Ok(ClockValue {
num_ticks: syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0)?
as isize,
clock_frequency: self.clock_frequency,
})
}
pub fn stop_alarm(&mut self, alarm: Alarm) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, alarm.alarm_id, 0)?;
Ok(())
}
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<Alarm> {
let now = self.get_current_clock()?;
let freq = self.clock_frequency.hz();
let duration_ms = duration.ms() as usize;
let ticks = match duration_ms.checked_mul(freq) {
Some(x) => x / 1000,
None => {
// Divide the largest of the two operands by 1000, to improve precision of the
// result.
if duration_ms > freq {
match (duration_ms / 1000).checked_mul(freq) {
Some(y) => y,
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
}
} else {
match (freq / 1000).checked_mul(duration_ms) {
Some(y) => y,
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
}
}
}
};
let alarm_instant = now.num_ticks() as usize + ticks;
let alarm_id = syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, alarm_instant, 0)?;
Ok(Alarm { alarm_id })
}
}
#[derive(Copy, Clone, Debug)]
pub struct ClockFrequency {
hz: usize,
}
impl ClockFrequency {
pub fn hz(self) -> usize {
self.hz
}
}
#[derive(Copy, Clone, Debug)]
pub struct ClockValue {
num_ticks: isize,
clock_frequency: ClockFrequency,
}
impl ClockValue {
pub fn num_ticks(self) -> isize {
self.num_ticks
}
pub fn ms(self) -> isize {
if self.num_ticks.abs() < isize::MAX / 1000 {
(1000 * self.num_ticks) / self.clock_frequency.hz() as isize
} else {
1000 * (self.num_ticks / self.clock_frequency.hz() as isize)
}
}
pub fn ms_f64(self) -> f64 {
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() as f64)
}
}
pub struct Alarm {
alarm_id: usize,
}
impl Alarm {
pub fn alarm_id(&self) -> usize {
self.alarm_id
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Duration<T> {
ms: T,
}
impl<T> Duration<T> {
pub const fn from_ms(ms: T) -> Duration<T> {
Duration { ms }
}
}
impl<T> Duration<T>
where
T: Copy,
{
pub fn ms(&self) -> T {
self.ms
}
}
impl<T> Sub for Duration<T>
where
T: Sub<Output = T>,
{
type Output = Duration<T>;
fn sub(self, other: Duration<T>) -> Duration<T> {
Duration {
ms: self.ms - other.ms,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Timestamp<T> {
ms: T,
}
impl<T> Timestamp<T> {
pub const fn from_ms(ms: T) -> Timestamp<T> {
Timestamp { ms }
}
}
impl<T> Timestamp<T>
where
T: Copy,
{
pub fn ms(&self) -> T {
self.ms
}
}
impl Timestamp<isize> {
pub fn from_clock_value(value: ClockValue) -> Timestamp<isize> {
Timestamp { ms: value.ms() }
}
}
impl Timestamp<f64> {
pub fn from_clock_value(value: ClockValue) -> Timestamp<f64> {
Timestamp { ms: value.ms_f64() }
}
}
impl<T> Sub for Timestamp<T>
where
T: Sub<Output = T>,
{
type Output = Duration<T>;
fn sub(self, other: Timestamp<T>) -> Duration<T> {
Duration::from_ms(self.ms - other.ms)
}
}
impl<T> Add<Duration<T>> for Timestamp<T>
where
T: Copy + Add<Output = T>,
{
type Output = Timestamp<T>;
fn add(self, duration: Duration<T>) -> Timestamp<T> {
Timestamp {
ms: self.ms + duration.ms(),
}
}
}
impl<T> AddAssign<Duration<T>> for Timestamp<T>
where
T: Copy + AddAssign,
{
fn add_assign(&mut self, duration: Duration<T>) {
self.ms += duration.ms();
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub(crate) struct ActiveTimer {
instant: u32,
set_at: u32,
}
/// Context for the time driver.
/// You can create a context as follows:
/// ```no_run
/// # use libtock::result::TockResult;
/// # async fn doc() -> TockResult<()> {
/// let mut drivers = libtock::retrieve_drivers()?;
/// let mut timer_context = drivers.timer;
/// # Ok(())
/// # }
/// ```
#[non_exhaustive]
pub struct DriverContext {
pub(crate) active_timer: Cell<Option<ActiveTimer>>,
}
impl DriverContext {
/// Create a driver timer from a context.
pub fn create_timer_driver(&mut self) -> TimerDriver {
TimerDriver {
callback: Callback,
context: self,
}
}
pub fn with_callback<CB>(&mut self, callback: CB) -> WithCallback<CB> {
WithCallback {
callback,
clock_frequency: ClockFrequency { hz: 0 },
phantom: PhantomData,
}
}
}
/// Timer driver instance. You can create a TimerDriver from a DriverContext as follows:
/// ```no_run
/// # use libtock::result::TockResult;
/// # async fn doc() -> TockResult<()> {
/// # let mut drivers = libtock::retrieve_drivers()?;
/// # let mut timer_context = drivers.timer;
/// let mut timer_driver = timer_context.create_timer_driver();
/// let timer_driver = timer_driver.activate()?;
/// # Ok(())
/// # }
/// ```
pub struct TimerDriver<'a> {
callback: Callback,
context: &'a DriverContext,
}
struct Callback;
struct ParallelTimerConsumer;
impl<'a> Consumer<Callback> for ParallelTimerConsumer {
fn consume(_: &mut Callback, _: usize, _: usize, _: usize) {}
}
/// Activated time driver. Updates current time in the context and manages
/// active alarms.
/// Example usage (sleep for 1 second):
/// ```no_run
/// # use libtock::result::TockResult;
/// # use libtock::timer::Duration;
/// # async fn doc() -> TockResult<()> {
/// # let mut drivers = libtock::retrieve_drivers()?;
/// # let mut timer_driver = drivers.timer.create_timer_driver();
/// let timer_driver = timer_driver.activate()?;
/// timer_driver.sleep(Duration::from_ms(1000)).await?;
/// # Ok(())
/// # }
/// ```
pub struct ParallelSleepDriver<'a> {
_callback_subscription: CallbackSubscription<'a>,
context: &'a DriverContext,
}
impl<'a> TimerDriver<'a> {
/// Activate the timer driver, will return a ParallelSleepDriver which
/// can used to sleep.
pub fn activate(&'a mut self) -> TockResult<ParallelSleepDriver<'a>> {
let subscription = syscalls::subscribe::<ParallelTimerConsumer, _>(
DRIVER_NUMBER,
subscribe_nr::SUBSCRIBE_CALLBACK,
&mut self.callback,
)?;
let driver = ParallelSleepDriver {
_callback_subscription: subscription,
context: &self.context,
};
Ok(driver)
}
}
impl<'a> ParallelSleepDriver<'a> {
/// Sleep for the given duration
pub async fn sleep(&self, duration: Duration<usize>) -> TockResult<()> {
let now = get_current_ticks()?;
let freq = get_clock_frequency()?;
let alarm_instant = Self::compute_alarm_instant(duration.ms, now, freq)?;
let this_alarm = ActiveTimer {
instant: alarm_instant as u32,
set_at: now as u32,
};
let suspended_timer: Cell<Option<ActiveTimer>> = Cell::new(None);
futures::wait_until(|| {
self.activate_current_timer(this_alarm, &suspended_timer)
.unwrap_or(false)
})
.await;
Ok(())
}
fn activate_timer(&self, timer: ActiveTimer) -> TockResult<()> {
set_alarm_at(timer.instant as usize)?;
let now = get_current_ticks()?;
if !is_over(timer, now as u32) {
self.context.active_timer.set(Some(timer));
} else {
self.wakeup_soon()?;
}
Ok(())
}
fn wakeup_soon(&self) -> TockResult<()> {
self.context.active_timer.set(None);
for i in 0.. {
let now = get_current_ticks()?;
let next_timer = ActiveTimer {
instant: now as u32 + i,
set_at: now as u32,
};
set_alarm_at(next_timer.instant as usize)?;
let now = get_current_ticks()?;
if !is_over(next_timer, now as u32) {
break;
} else {
stop_alarm_at(next_timer.instant as usize)?;
}
}
Ok(())
}
fn compute_alarm_instant(
duration_ms: usize,
num_ticks: usize,
freq: usize,
) -> TockResult<usize> {
let ticks = match duration_ms.checked_mul(freq) {
Some(x) => x / 1000,
None => {
// Divide the largest of the two operands by 1000, to improve precision of the
// result.
if duration_ms > freq {
match (duration_ms / 1000).checked_mul(freq) {
Some(y) => y,
None => {
return Err(TockError::Other(OtherError::TimerDriverDurationOutOfRange))
}
}
} else {
match (freq / 1000).checked_mul(duration_ms) {
Some(y) => y,
None => {
return Err(TockError::Other(OtherError::TimerDriverDurationOutOfRange))
}
}
}
}
};
let alarm_instant = num_ticks + ticks;
Ok(alarm_instant)
}
fn activate_current_timer(
&self,
this_alarm: ActiveTimer,
suspended_timer: &Cell<Option<ActiveTimer>>,
) -> TockResult<bool> {
let now = get_current_ticks()?;
if let Some(active) = self.context.active_timer.get() {
if left_is_later(active, this_alarm) {
suspended_timer.set(Some(active));
self.activate_timer(this_alarm)?;
}
} else {
self.activate_timer(this_alarm)?;
}
if is_over(this_alarm, now as u32) {
if let Some(paused) = suspended_timer.get() {
self.activate_timer(paused)?;
} else {
self.context.active_timer.set(None);
}
Ok(true)
} else {
Ok(false)
}
}
}
fn get_current_ticks() -> TockResult<usize> {
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0).map_err(|err| err.into())
}
fn set_alarm_at(instant: usize) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, instant, 0)
.map(|_| ())
.map_err(|err| err.into())
}
fn stop_alarm_at(instant: usize) -> TockResult<()> {
match syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, instant, 0) {
Ok(_) => Ok(()),
Err(error) => match error.return_code {
EALREADY => Ok(()),
_ => Err(TockError::Command(error)),
},
}
}
fn get_clock_frequency() -> TockResult<usize> {
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)
.map_err(|err| err.into())
}
fn is_over(timer: ActiveTimer, now: u32) -> bool {
now.wrapping_sub(timer.set_at) >= timer.instant.wrapping_sub(timer.set_at)
}
fn left_is_later(alarm_1: ActiveTimer, alarm_2: ActiveTimer) -> bool {
if alarm_1.set_at <= alarm_1.instant && alarm_2.set_at <= alarm_2.instant {
return alarm_1.instant > alarm_2.instant;
}
if alarm_1.set_at <= alarm_1.instant && alarm_2.set_at >= alarm_2.instant {
return false;
}
if alarm_1.set_at >= alarm_1.instant && alarm_2.set_at <= alarm_2.instant {
return true;
}
if alarm_1.set_at >= alarm_1.instant && alarm_2.set_at >= alarm_2.instant {
return alarm_1.instant > alarm_2.instant;
}
false
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn duration_bigger_than_frequency() {
let x = ParallelSleepDriver::compute_alarm_instant(10000, 0, 1000)
.ok()
.unwrap();
assert_eq!(x, 10000);
}
#[test]
pub fn frequency_bigger_than_duration() {
let x = ParallelSleepDriver::compute_alarm_instant(1000, 0, 10000)
.ok()
.unwrap();
assert_eq!(x, 10000);
}
#[test]
pub fn fails_if_duration_is_too_large() {
let x =
ParallelSleepDriver::compute_alarm_instant(core::usize::MAX, 0, core::usize::MAX - 1);
assert!(x.is_err());
}
#[test]
pub fn fails_if_frequency_is_too_large() {
let x =
ParallelSleepDriver::compute_alarm_instant(core::usize::MAX - 1, 0, core::usize::MAX);
assert!(x.is_err());
}
#[test]
pub fn alarm_before_systick_wrap_expired() {
assert_eq!(
super::is_over(
super::ActiveTimer {
instant: 2u32,
set_at: 1u32
},
3u32
),
true
);
}
#[test]
pub fn alarm_before_systick_wrap_not_expired() {
assert_eq!(
super::is_over(
super::ActiveTimer {
instant: 3u32,
set_at: 1u32
},
2u32
),
false
);
}
#[test]
pub fn alarm_after_systick_wrap_expired() {
assert_eq!(
super::is_over(
super::ActiveTimer {
instant: 1u32,
set_at: 3u32
},
2u32
),
true
);
}
#[test]
pub fn alarm_after_systick_wrap_time_before_systick_wrap_not_expired() {
assert_eq!(
super::is_over(
super::ActiveTimer {
instant: 1u32,
set_at: 3u32
},
4u32
),
false
);
}
#[test]
pub fn alarm_after_systick_wrap_time_after_systick_wrap_not_expired() {
assert_eq!(
super::is_over(
super::ActiveTimer {
instant: 1u32,
set_at: 3u32
},
0u32
),
false
);
}
#[test]
pub fn left_later_than_the_other_both_not_wrapped() {
let later = super::ActiveTimer {
instant: 3u32,
set_at: 1u32,
};
let earlier = super::ActiveTimer {
instant: 2u32,
set_at: 1u32,
};
assert_eq!(super::left_is_later(later, earlier), true);
}
#[test]
pub fn right_later_than_the_other_both_not_wrapped() {
let later = super::ActiveTimer {
instant: 2u32,
set_at: 1u32,
};
let earlier = super::ActiveTimer {
instant: 3u32,
set_at: 1u32,
};
assert_eq!(super::left_is_later(later, earlier), false);
}
#[test]
pub fn left_later_left_wrapped() {
let later = super::ActiveTimer {
instant: 1u32,
set_at: 3u32,
};
let earlier = super::ActiveTimer {
instant: 2u32,
set_at: 1u32,
};
assert_eq!(super::left_is_later(later, earlier), true);
}
#[test]
pub fn right_later_right_wrapped() {
let later = super::ActiveTimer {
instant: 3u32,
set_at: 1u32,
};
let earlier = super::ActiveTimer {
instant: 1u32,
set_at: 3u32,
};
assert_eq!(super::left_is_later(later, earlier), false);
}
#[test]
pub fn left_later_both_wrapped() {
let later = super::ActiveTimer {
instant: 2u32,
set_at: 3u32,
};
let earlier = super::ActiveTimer {
instant: 1u32,
set_at: 3u32,
};
assert_eq!(super::left_is_later(later, earlier), true);
}
#[test]
pub fn right_later_both_wrapped() {
let later = super::ActiveTimer {
instant: 1u32,
set_at: 3u32,
};
let earlier = super::ActiveTimer {
instant: 2u32,
set_at: 3u32,
};
assert_eq!(super::left_is_later(later, earlier), false);
}
#[test]
pub fn inequality_is_strict() {
let later = super::ActiveTimer {
instant: 2u32,
set_at: 1u32,
};
let earlier = super::ActiveTimer {
instant: 2u32,
set_at: 1u32,
};
assert_eq!(super::left_is_later(later, earlier), false);
}
#[test]
pub fn inequality_is_strict_wrapped() {
let later = super::ActiveTimer {
instant: 1u32,
set_at: 2u32,
};
let earlier = super::ActiveTimer {
instant: 1u32,
set_at: 2u32,
};
assert_eq!(super::left_is_later(later, earlier), false);
}
}