Update third_party/libtock-drivers to support OpenSK.
This commit is contained in:
562
third_party/libtock-drivers/src/timer.rs
vendored
562
third_party/libtock-drivers/src/timer.rs
vendored
@@ -1,17 +1,12 @@
|
||||
//! 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 crate::result::{FlexUnwrap, OtherError, TockError, TockResult};
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use core::isize;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, Sub};
|
||||
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||
use libtock_core::result::{CommandError, EALREADY};
|
||||
use libtock_core::syscalls;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00000;
|
||||
|
||||
@@ -27,6 +22,33 @@ mod subscribe_nr {
|
||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||
}
|
||||
|
||||
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
|
||||
let expired = Cell::new(false);
|
||||
let mut with_callback = with_callback(|_, _| expired.set(true));
|
||||
|
||||
let mut timer = with_callback.init().flex_unwrap();
|
||||
let timer_alarm = timer.set_alarm(duration).flex_unwrap();
|
||||
|
||||
util::yieldk_for(|| expired.get());
|
||||
|
||||
match timer.stop_alarm(timer_alarm) {
|
||||
Ok(())
|
||||
| Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
|
||||
WithCallback {
|
||||
callback,
|
||||
clock_frequency: ClockFrequency { hz: 0 },
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WithCallback<'a, CB> {
|
||||
callback: CB,
|
||||
clock_frequency: ClockFrequency,
|
||||
@@ -136,13 +158,13 @@ impl<'a> Timer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct ClockFrequency {
|
||||
hz: usize,
|
||||
}
|
||||
|
||||
impl ClockFrequency {
|
||||
pub fn hz(self) -> usize {
|
||||
pub fn hz(&self) -> usize {
|
||||
self.hz
|
||||
}
|
||||
}
|
||||
@@ -154,21 +176,53 @@ pub struct ClockValue {
|
||||
}
|
||||
|
||||
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 const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
|
||||
ClockValue {
|
||||
num_ticks,
|
||||
clock_frequency: ClockFrequency { hz: clock_hz },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ms_f64(self) -> f64 {
|
||||
pub fn num_ticks(&self) -> isize {
|
||||
self.num_ticks
|
||||
}
|
||||
|
||||
// Computes (value * factor) / divisor, even when value * factor >= isize::MAX.
|
||||
fn scale_int(value: isize, factor: isize, divisor: isize) -> isize {
|
||||
// As long as isize is not i64, this should be fine. If not, this is an alternative:
|
||||
// factor * (value / divisor) + ((value % divisor) * factor) / divisor
|
||||
((value as i64 * factor as i64) / divisor as i64) as isize
|
||||
}
|
||||
|
||||
pub fn ms(&self) -> isize {
|
||||
ClockValue::scale_int(self.num_ticks, 1000, 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 fn wrapping_add(self, duration: Duration<isize>) -> ClockValue {
|
||||
// This is a precision preserving formula for scaling an isize.
|
||||
let duration_ticks =
|
||||
ClockValue::scale_int(duration.ms, self.clock_frequency.hz() as isize, 1000);
|
||||
ClockValue {
|
||||
num_ticks: self.num_ticks.wrapping_add(duration_ticks),
|
||||
clock_frequency: self.clock_frequency,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrapping_sub(self, other: ClockValue) -> Option<Duration<isize>> {
|
||||
if self.clock_frequency == other.clock_frequency {
|
||||
let clock_duration = ClockValue {
|
||||
num_ticks: self.num_ticks - other.num_ticks,
|
||||
clock_frequency: self.clock_frequency,
|
||||
};
|
||||
Some(Duration::from_ms(clock_duration.ms()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alarm {
|
||||
@@ -278,467 +332,3 @@ where
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user