From 5daa173ce4b153a532b4daa9e94c7a248231f25b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 17 Aug 2022 23:40:16 +0200 Subject: Split embassy-time from embassy-executor. --- embassy-executor/src/time/delay.rs | 98 --------------- embassy-executor/src/time/driver.rs | 170 ------------------------- embassy-executor/src/time/driver_std.rs | 208 ------------------------------- embassy-executor/src/time/driver_wasm.rs | 134 -------------------- embassy-executor/src/time/duration.rs | 184 --------------------------- embassy-executor/src/time/instant.rs | 159 ----------------------- embassy-executor/src/time/mod.rs | 91 -------------- embassy-executor/src/time/timer.rs | 151 ---------------------- 8 files changed, 1195 deletions(-) delete mode 100644 embassy-executor/src/time/delay.rs delete mode 100644 embassy-executor/src/time/driver.rs delete mode 100644 embassy-executor/src/time/driver_std.rs delete mode 100644 embassy-executor/src/time/driver_wasm.rs delete mode 100644 embassy-executor/src/time/duration.rs delete mode 100644 embassy-executor/src/time/instant.rs delete mode 100644 embassy-executor/src/time/mod.rs delete mode 100644 embassy-executor/src/time/timer.rs (limited to 'embassy-executor/src/time') diff --git a/embassy-executor/src/time/delay.rs b/embassy-executor/src/time/delay.rs deleted file mode 100644 index d76ed32eb..000000000 --- a/embassy-executor/src/time/delay.rs +++ /dev/null @@ -1,98 +0,0 @@ -use super::{Duration, Instant}; - -/// Blocks for at least `duration`. -pub fn block_for(duration: Duration) { - let expires_at = Instant::now() + duration; - while Instant::now() < expires_at {} -} - -/// Type implementing async delays and blocking `embedded-hal` delays. -/// -/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least -/// the amount provided, but accuracy can be affected by many factors, including interrupt usage. -/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently -/// active driver. -pub struct Delay; - -#[cfg(feature = "unstable-traits")] -mod eh1 { - use super::*; - - impl embedded_hal_1::delay::blocking::DelayUs for Delay { - type Error = core::convert::Infallible; - - fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_micros(us as u64))) - } - - fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_millis(ms as u64))) - } - } -} - -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use crate::time::Timer; - use core::future::Future; - use futures_util::FutureExt; - - impl embedded_hal_async::delay::DelayUs for Delay { - type Error = core::convert::Infallible; - - type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { - Timer::after(Duration::from_micros(micros as _)).map(Ok) - } - - type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { - Timer::after(Duration::from_millis(millis as _)).map(Ok) - } - } - } -} - -mod eh02 { - use embedded_hal_02::blocking::delay::{DelayMs, DelayUs}; - - use super::*; - - impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u8) { - block_for(Duration::from_millis(ms as u64)) - } - } - - impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u16) { - block_for(Duration::from_millis(ms as u64)) - } - } - - impl DelayMs for Delay { - fn delay_ms(&mut self, ms: u32) { - block_for(Duration::from_millis(ms as u64)) - } - } - - impl DelayUs for Delay { - fn delay_us(&mut self, us: u8) { - block_for(Duration::from_micros(us as u64)) - } - } - - impl DelayUs for Delay { - fn delay_us(&mut self, us: u16) { - block_for(Duration::from_micros(us as u64)) - } - } - - impl DelayUs for Delay { - fn delay_us(&mut self, us: u32) { - block_for(Duration::from_micros(us as u64)) - } - } -} diff --git a/embassy-executor/src/time/driver.rs b/embassy-executor/src/time/driver.rs deleted file mode 100644 index 48e2f1c7d..000000000 --- a/embassy-executor/src/time/driver.rs +++ /dev/null @@ -1,170 +0,0 @@ -//! Time driver interface -//! -//! This module defines the interface a driver needs to implement to power the `embassy_executor::time` module. -//! -//! # Implementing a driver -//! -//! - Define a struct `MyDriver` -//! - Implement [`Driver`] for it -//! - Register it as the global driver with [`time_driver_impl`]. -//! - Enable the Cargo features `embassy-executor/time` and one of `embassy-executor/time-tick-*` corresponding to the -//! tick rate of your driver. -//! -//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own -//! Cargo features and having each enable the corresponding `embassy-executor/time-tick-*`. -//! -//! # Linkage details -//! -//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. -//! -//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. -//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the -//! calls from the `embassy` crate to call into the driver crate. -//! -//! If there is none or multiple drivers in the crate tree, linking will fail. -//! -//! This method has a few key advantages for something as foundational as timekeeping: -//! -//! - The time driver is available everywhere easily, without having to thread the implementation -//! through generic parameters. This is especially helpful for libraries. -//! - It means comparing `Instant`s will always make sense: if there were multiple drivers -//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which -//! would yield incorrect results. -//! -//! # Example -//! -//! ``` -//! use embassy_executor::time::driver::{Driver, AlarmHandle}; -//! -//! struct MyDriver{}; // not public! -//! embassy_executor::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); -//! -//! impl Driver for MyDriver { -//! fn now(&self) -> u64 { -//! todo!() -//! } -//! unsafe fn allocate_alarm(&self) -> Option { -//! todo!() -//! } -//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { -//! todo!() -//! } -//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { -//! todo!() -//! } -//! } -//! ``` - -/// Alarm handle, assigned by the driver. -#[derive(Clone, Copy)] -pub struct AlarmHandle { - id: u8, -} - -impl AlarmHandle { - /// Create an AlarmHandle - /// - /// Safety: May only be called by the current global Driver impl. - /// The impl is allowed to rely on the fact that all `AlarmHandle` instances - /// are created by itself in unsafe code (e.g. indexing operations) - pub unsafe fn new(id: u8) -> Self { - Self { id } - } - - /// Get the ID of the AlarmHandle. - pub fn id(&self) -> u8 { - self.id - } -} - -/// Time driver -pub trait Driver: Send + Sync + 'static { - /// Return the current timestamp in ticks. - /// - /// Implementations MUST ensure that: - /// - This is guaranteed to be monotonic, i.e. a call to now() will always return - /// a greater or equal value than earler calls. Time can't "roll backwards". - /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say - /// in 10_000 years (Human civilization is likely to already have self-destructed - /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers - /// you MUST extend them to 64-bit, for example by counting overflows in software, - /// or chaining multiple timers together. - fn now(&self) -> u64; - - /// Try allocating an alarm handle. Returns None if no alarms left. - /// Initially the alarm has no callback set, and a null `ctx` pointer. - /// - /// # Safety - /// It is UB to make the alarm fire before setting a callback. - unsafe fn allocate_alarm(&self) -> Option; - - /// Sets the callback function to be called when the alarm triggers. - /// The callback may be called from any context (interrupt or thread mode). - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - - /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm - /// timestamp, the provided callback function will be called. - /// - /// If `timestamp` is already in the past, the alarm callback must be immediately fired. - /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. - /// - /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. - /// - /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); -} - -extern "Rust" { - fn _embassy_time_now() -> u64; - fn _embassy_time_allocate_alarm() -> Option; - fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); -} - -pub(crate) fn now() -> u64 { - unsafe { _embassy_time_now() } -} -/// Safety: it is UB to make the alarm fire before setting a callback. -pub(crate) unsafe fn allocate_alarm() -> Option { - _embassy_time_allocate_alarm() -} -pub(crate) fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } -} -pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { - unsafe { _embassy_time_set_alarm(alarm, timestamp) } -} - -/// Set the time Driver implementation. -/// -/// See the module documentation for an example. -#[macro_export] -macro_rules! time_driver_impl { - (static $name:ident: $t: ty = $val:expr) => { - static $name: $t = $val; - - #[no_mangle] - fn _embassy_time_now() -> u64 { - <$t as $crate::time::driver::Driver>::now(&$name) - } - - #[no_mangle] - unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> { - <$t as $crate::time::driver::Driver>::allocate_alarm(&$name) - } - - #[no_mangle] - fn _embassy_time_set_alarm_callback( - alarm: $crate::time::driver::AlarmHandle, - callback: fn(*mut ()), - ctx: *mut (), - ) { - <$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) - } - - #[no_mangle] - fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) { - <$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp) - } - }; -} diff --git a/embassy-executor/src/time/driver_std.rs b/embassy-executor/src/time/driver_std.rs deleted file mode 100644 index cb66f7c19..000000000 --- a/embassy-executor/src/time/driver_std.rs +++ /dev/null @@ -1,208 +0,0 @@ -use std::cell::UnsafeCell; -use std::mem::MaybeUninit; -use std::sync::{Condvar, Mutex, Once}; -use std::time::{Duration as StdDuration, Instant as StdInstant}; -use std::{mem, ptr, thread}; - -use atomic_polyfill::{AtomicU8, Ordering}; - -use crate::time::driver::{AlarmHandle, Driver}; - -const ALARM_COUNT: usize = 4; - -struct AlarmState { - timestamp: u64, - - // This is really a Option<(fn(*mut ()), *mut ())> - // but fn pointers aren't allowed in const yet - callback: *const (), - ctx: *mut (), -} - -unsafe impl Send for AlarmState {} - -impl AlarmState { - const fn new() -> Self { - Self { - timestamp: u64::MAX, - callback: ptr::null(), - ctx: ptr::null_mut(), - } - } -} - -struct TimeDriver { - alarm_count: AtomicU8, - - once: Once, - alarms: UninitCell>, - zero_instant: UninitCell, - signaler: UninitCell, -} - -const ALARM_NEW: AlarmState = AlarmState::new(); -crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - alarm_count: AtomicU8::new(0), - - once: Once::new(), - alarms: UninitCell::uninit(), - zero_instant: UninitCell::uninit(), - signaler: UninitCell::uninit(), -}); - -impl TimeDriver { - fn init(&self) { - self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); - self.zero_instant.write(StdInstant::now()); - self.signaler.write(Signaler::new()); - - thread::spawn(Self::alarm_thread); - }); - } - - fn alarm_thread() { - let zero = unsafe { DRIVER.zero_instant.read() }; - loop { - let now = DRIVER.now(); - - let mut next_alarm = u64::MAX; - { - let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); - for alarm in alarms { - if alarm.timestamp <= now { - alarm.timestamp = u64::MAX; - - // Call after clearing alarm, so the callback can set another alarm. - - // safety: - // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. - // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; - f(alarm.ctx); - } else { - next_alarm = next_alarm.min(alarm.timestamp); - } - } - } - - // Ensure we don't overflow - let until = zero - .checked_add(StdDuration::from_micros(next_alarm)) - .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1)); - - unsafe { DRIVER.signaler.as_ref() }.wait_until(until); - } - } -} - -impl Driver for TimeDriver { - fn now(&self) -> u64 { - self.init(); - - let zero = unsafe { self.zero_instant.read() }; - StdInstant::now().duration_since(zero).as_micros() as u64 - } - - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.callback = callback as *const (); - alarm.ctx = ctx; - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.timestamp = timestamp; - unsafe { self.signaler.as_ref() }.signal(); - } -} - -struct Signaler { - mutex: Mutex, - condvar: Condvar, -} - -impl Signaler { - fn new() -> Self { - Self { - mutex: Mutex::new(false), - condvar: Condvar::new(), - } - } - - fn wait_until(&self, until: StdInstant) { - let mut signaled = self.mutex.lock().unwrap(); - while !*signaled { - let now = StdInstant::now(); - - if now >= until { - break; - } - - let dur = until - now; - let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap(); - signaled = signaled2; - if timeout.timed_out() { - break; - } - } - *signaled = false; - } - - fn signal(&self) { - let mut signaled = self.mutex.lock().unwrap(); - *signaled = true; - self.condvar.notify_one(); - } -} - -pub(crate) struct UninitCell(MaybeUninit>); -unsafe impl Send for UninitCell {} -unsafe impl Sync for UninitCell {} - -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - - pub unsafe fn as_ptr(&self) -> *const T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) - } -} - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} diff --git a/embassy-executor/src/time/driver_wasm.rs b/embassy-executor/src/time/driver_wasm.rs deleted file mode 100644 index 5f585a19a..000000000 --- a/embassy-executor/src/time/driver_wasm.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::cell::UnsafeCell; -use std::mem::MaybeUninit; -use std::ptr; -use std::sync::{Mutex, Once}; - -use atomic_polyfill::{AtomicU8, Ordering}; -use wasm_bindgen::prelude::*; -use wasm_timer::Instant as StdInstant; - -use crate::time::driver::{AlarmHandle, Driver}; - -const ALARM_COUNT: usize = 4; - -struct AlarmState { - token: Option, - closure: Option>, -} - -unsafe impl Send for AlarmState {} - -impl AlarmState { - const fn new() -> Self { - Self { - token: None, - closure: None, - } - } -} - -#[wasm_bindgen] -extern "C" { - fn setTimeout(closure: &Closure, millis: u32) -> f64; - fn clearTimeout(token: f64); -} - -struct TimeDriver { - alarm_count: AtomicU8, - - once: Once, - alarms: UninitCell>, - zero_instant: UninitCell, -} - -const ALARM_NEW: AlarmState = AlarmState::new(); -crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - alarm_count: AtomicU8::new(0), - once: Once::new(), - alarms: UninitCell::uninit(), - zero_instant: UninitCell::uninit(), -}); - -impl TimeDriver { - fn init(&self) { - self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); - self.zero_instant.write(StdInstant::now()); - }); - } -} - -impl Driver for TimeDriver { - fn now(&self) -> u64 { - self.init(); - - let zero = unsafe { self.zero_instant.read() }; - StdInstant::now().duration_since(zero).as_micros() as u64 - } - - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.closure.replace(Closure::new(move || { - callback(ctx); - })); - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - let timeout = (timestamp - self.now()) as u32; - if let Some(token) = alarm.token { - clearTimeout(token); - } - alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); - } -} - -pub(crate) struct UninitCell(MaybeUninit>); -unsafe impl Send for UninitCell {} -unsafe impl Sync for UninitCell {} - -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - unsafe fn as_ptr(&self) -> *const T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) - } -} - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} diff --git a/embassy-executor/src/time/duration.rs b/embassy-executor/src/time/duration.rs deleted file mode 100644 index dc4f16bd4..000000000 --- a/embassy-executor/src/time/duration.rs +++ /dev/null @@ -1,184 +0,0 @@ -use core::fmt; -use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; - -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Represents the difference between two [Instant](struct.Instant.html)s -pub struct Duration { - pub(crate) ticks: u64, -} - -impl Duration { - /// The smallest value that can be represented by the `Duration` type. - pub const MIN: Duration = Duration { ticks: u64::MIN }; - /// The largest value that can be represented by the `Duration` type. - pub const MAX: Duration = Duration { ticks: u64::MAX }; - - /// Tick count of the `Duration`. - pub const fn as_ticks(&self) -> u64 { - self.ticks - } - - /// Convert the `Duration` to seconds, rounding down. - pub const fn as_secs(&self) -> u64 { - self.ticks / TICKS_PER_SECOND - } - - /// Convert the `Duration` to milliseconds, rounding down. - pub const fn as_millis(&self) -> u64 { - self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) - } - - /// Convert the `Duration` to microseconds, rounding down. - pub const fn as_micros(&self) -> u64 { - self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) - } - - /// Creates a duration from the specified number of clock ticks - pub const fn from_ticks(ticks: u64) -> Duration { - Duration { ticks } - } - - /// Creates a duration from the specified number of seconds, rounding up. - pub const fn from_secs(secs: u64) -> Duration { - Duration { - ticks: secs * TICKS_PER_SECOND, - } - } - - /// Creates a duration from the specified number of milliseconds, rounding up. - pub const fn from_millis(millis: u64) -> Duration { - Duration { - ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K), - } - } - - /// Creates a duration from the specified number of microseconds, rounding up. - /// NOTE: Delays this small may be inaccurate. - pub const fn from_micros(micros: u64) -> Duration { - Duration { - ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M), - } - } - - /// Creates a duration from the specified number of seconds, rounding down. - pub const fn from_secs_floor(secs: u64) -> Duration { - Duration { - ticks: secs * TICKS_PER_SECOND, - } - } - - /// Creates a duration from the specified number of milliseconds, rounding down. - pub const fn from_millis_floor(millis: u64) -> Duration { - Duration { - ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), - } - } - - /// Creates a duration from the specified number of microseconds, rounding down. - /// NOTE: Delays this small may be inaccurate. - pub const fn from_micros_floor(micros: u64) -> Duration { - Duration { - ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), - } - } - - /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. - pub fn checked_add(self, rhs: Duration) -> Option { - self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks }) - } - - /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow. - pub fn checked_sub(self, rhs: Duration) -> Option { - self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks }) - } - - /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow. - pub fn checked_mul(self, rhs: u32) -> Option { - self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks }) - } - - /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow. - pub fn checked_div(self, rhs: u32) -> Option { - self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks }) - } -} - -impl Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Duration { - self.checked_add(rhs).expect("overflow when adding durations") - } -} - -impl AddAssign for Duration { - fn add_assign(&mut self, rhs: Duration) { - *self = *self + rhs; - } -} - -impl Sub for Duration { - type Output = Duration; - - fn sub(self, rhs: Duration) -> Duration { - self.checked_sub(rhs).expect("overflow when subtracting durations") - } -} - -impl SubAssign for Duration { - fn sub_assign(&mut self, rhs: Duration) { - *self = *self - rhs; - } -} - -impl Mul for Duration { - type Output = Duration; - - fn mul(self, rhs: u32) -> Duration { - self.checked_mul(rhs) - .expect("overflow when multiplying duration by scalar") - } -} - -impl Mul for u32 { - type Output = Duration; - - fn mul(self, rhs: Duration) -> Duration { - rhs * self - } -} - -impl MulAssign for Duration { - fn mul_assign(&mut self, rhs: u32) { - *self = *self * rhs; - } -} - -impl Div for Duration { - type Output = Duration; - - fn div(self, rhs: u32) -> Duration { - self.checked_div(rhs) - .expect("divide by zero error when dividing duration by scalar") - } -} - -impl DivAssign for Duration { - fn div_assign(&mut self, rhs: u32) { - *self = *self / rhs; - } -} - -impl<'a> fmt::Display for Duration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} ticks", self.ticks) - } -} - -#[inline] -const fn div_ceil(num: u64, den: u64) -> u64 { - (num + den - 1) / den -} diff --git a/embassy-executor/src/time/instant.rs b/embassy-executor/src/time/instant.rs deleted file mode 100644 index 6a4925f47..000000000 --- a/embassy-executor/src/time/instant.rs +++ /dev/null @@ -1,159 +0,0 @@ -use core::fmt; -use core::ops::{Add, AddAssign, Sub, SubAssign}; - -use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// An Instant in time, based on the MCU's clock ticks since startup. -pub struct Instant { - ticks: u64, -} - -impl Instant { - /// The smallest (earliest) value that can be represented by the `Instant` type. - pub const MIN: Instant = Instant { ticks: u64::MIN }; - /// The largest (latest) value that can be represented by the `Instant` type. - pub const MAX: Instant = Instant { ticks: u64::MAX }; - - /// Returns an Instant representing the current time. - pub fn now() -> Instant { - Instant { ticks: driver::now() } - } - - /// Create an Instant from a tick count since system boot. - pub const fn from_ticks(ticks: u64) -> Self { - Self { ticks } - } - - /// Create an Instant from a microsecond count since system boot. - pub const fn from_micros(micros: u64) -> Self { - Self { - ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), - } - } - - /// Create an Instant from a millisecond count since system boot. - pub const fn from_millis(millis: u64) -> Self { - Self { - ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), - } - } - - /// Create an Instant from a second count since system boot. - pub const fn from_secs(seconds: u64) -> Self { - Self { - ticks: seconds * TICKS_PER_SECOND, - } - } - - /// Tick count since system boot. - pub const fn as_ticks(&self) -> u64 { - self.ticks - } - - /// Seconds since system boot. - pub const fn as_secs(&self) -> u64 { - self.ticks / TICKS_PER_SECOND - } - - /// Milliseconds since system boot. - pub const fn as_millis(&self) -> u64 { - self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) - } - - /// Microseconds since system boot. - pub const fn as_micros(&self) -> u64 { - self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) - } - - /// Duration between this Instant and another Instant - /// Panics on over/underflow. - pub fn duration_since(&self, earlier: Instant) -> Duration { - Duration { - ticks: self.ticks.checked_sub(earlier.ticks).unwrap(), - } - } - - /// Duration between this Instant and another Instant - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - if self.ticks < earlier.ticks { - None - } else { - Some(Duration { - ticks: self.ticks - earlier.ticks, - }) - } - } - - /// Returns the duration since the "earlier" Instant. - /// If the "earlier" instant is in the future, the duration is set to zero. - pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { - Duration { - ticks: if self.ticks < earlier.ticks { - 0 - } else { - self.ticks - earlier.ticks - }, - } - } - - /// Duration elapsed since this Instant. - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow. - pub fn checked_add(&self, duration: Duration) -> Option { - self.ticks.checked_add(duration.ticks).map(|ticks| Instant { ticks }) - } - - /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow. - pub fn checked_sub(&self, duration: Duration) -> Option { - self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks }) - } -} - -impl Add for Instant { - type Output = Instant; - - fn add(self, other: Duration) -> Instant { - self.checked_add(other) - .expect("overflow when adding duration to instant") - } -} - -impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other) - .expect("overflow when subtracting duration from instant") - } -} - -impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -impl Sub for Instant { - type Output = Duration; - - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -impl<'a> fmt::Display for Instant { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} ticks", self.ticks) - } -} diff --git a/embassy-executor/src/time/mod.rs b/embassy-executor/src/time/mod.rs deleted file mode 100644 index b787a5cf2..000000000 --- a/embassy-executor/src/time/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Timekeeping, delays and timeouts. -//! -//! Timekeeping is done with elapsed time since system boot. Time is represented in -//! ticks, where the tick rate is defined by the current driver, usually to match -//! the tick rate of the hardware. -//! -//! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports -//! representing time spans of up to ~584558 years, which is big enough for all practical -//! purposes and allows not having to worry about overflows. -//! -//! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`] -//! represents the duration of a span of time. They implement the math operations you'd expect, -//! like addition and substraction. -//! -//! # Delays and timeouts -//! -//! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time. -//! -//! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility -//! with libraries from the ecosystem. -//! -//! # Wall-clock time -//! -//! The `time` module deals exclusively with a monotonically increasing tick count. -//! Therefore it has no direct support for wall-clock time ("real life" datetimes -//! like `2021-08-24 13:33:21`). -//! -//! If persistence across reboots is not needed, support can be built on top of -//! `embassy_executor::time` by storing the offset between "seconds elapsed since boot" -//! and "seconds since unix epoch". -//! -//! # Time driver -//! -//! The `time` module is backed by a global "time driver" specified at build time. -//! Only one driver can be active in a program. -//! -//! All methods and structs transparently call into the active driver. This makes it -//! possible for libraries to use `embassy_executor::time` in a driver-agnostic way without -//! requiring generic parameters. -//! -//! For more details, check the [`driver`] module. - -#![deny(missing_docs)] - -mod delay; -pub mod driver; -mod duration; -mod instant; -mod timer; - -#[cfg(feature = "std")] -mod driver_std; - -#[cfg(feature = "wasm")] -mod driver_wasm; - -pub use delay::{block_for, Delay}; -pub use duration::Duration; -pub use instant::Instant; -pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; - -#[cfg(feature = "time-tick-1000hz")] -const TPS: u64 = 1_000; - -#[cfg(feature = "time-tick-32768hz")] -const TPS: u64 = 32_768; - -#[cfg(feature = "time-tick-1mhz")] -const TPS: u64 = 1_000_000; - -#[cfg(feature = "time-tick-16mhz")] -const TPS: u64 = 16_000_000; - -/// Ticks per second of the global timebase. -/// -/// This value is specified by the `time-tick-*` Cargo features, which -/// should be set by the time driver. Some drivers support a fixed tick rate, others -/// allow you to choose a tick rate with Cargo features of their own. You should not -/// set the `time-tick-*` features for embassy yourself as an end user. -pub const TICKS_PER_SECOND: u64 = TPS; - -const fn gcd(a: u64, b: u64) -> u64 { - if b == 0 { - a - } else { - gcd(b, a % b) - } -} - -pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); -pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); diff --git a/embassy-executor/src/time/timer.rs b/embassy-executor/src/time/timer.rs deleted file mode 100644 index b9cdb1be5..000000000 --- a/embassy-executor/src/time/timer.rs +++ /dev/null @@ -1,151 +0,0 @@ -use core::future::Future; -use core::pin::Pin; -use core::task::{Context, Poll}; - -use futures_util::future::{select, Either}; -use futures_util::{pin_mut, Stream}; - -use crate::executor::raw; -use crate::time::{Duration, Instant}; - -/// Error returned by [`with_timeout`] on timeout. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TimeoutError; - -/// Runs a given future with a timeout. -/// -/// If the future completes before the timeout, its output is returned. Otherwise, on timeout, -/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. -pub async fn with_timeout(timeout: Duration, fut: F) -> Result { - let timeout_fut = Timer::after(timeout); - pin_mut!(fut); - match select(fut, timeout_fut).await { - Either::Left((r, _)) => Ok(r), - Either::Right(_) => Err(TimeoutError), - } -} - -/// A future that completes at a specified [Instant](struct.Instant.html). -pub struct Timer { - expires_at: Instant, - yielded_once: bool, -} - -impl Timer { - /// Expire at specified [Instant](struct.Instant.html) - pub fn at(expires_at: Instant) -> Self { - Self { - expires_at, - yielded_once: false, - } - } - - /// Expire after specified [Duration](struct.Duration.html). - /// This can be used as a `sleep` abstraction. - /// - /// Example: - /// ``` no_run - /// # #![feature(type_alias_impl_trait)] - /// # - /// # fn foo() {} - /// use embassy_executor::time::{Duration, Timer}; - /// - /// #[embassy_executor::task] - /// async fn demo_sleep_seconds() { - /// // suspend this task for one second. - /// Timer::after(Duration::from_secs(1)).await; - /// } - /// ``` - pub fn after(duration: Duration) -> Self { - Self { - expires_at: Instant::now() + duration, - yielded_once: false, - } - } -} - -impl Unpin for Timer {} - -impl Future for Timer { - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if self.yielded_once && self.expires_at <= Instant::now() { - Poll::Ready(()) - } else { - unsafe { raw::register_timer(self.expires_at, cx.waker()) }; - self.yielded_once = true; - Poll::Pending - } - } -} - -/// Asynchronous stream that yields every Duration, indefinitely. -/// -/// This stream will tick at uniform intervals, even if blocking work is performed between ticks. -/// -/// For instance, consider the following code fragment. -/// ``` no_run -/// # #![feature(type_alias_impl_trait)] -/// # -/// use embassy_executor::time::{Duration, Timer}; -/// # fn foo() {} -/// -/// #[embassy_executor::task] -/// async fn ticker_example_0() { -/// loop { -/// foo(); -/// Timer::after(Duration::from_secs(1)).await; -/// } -/// } -/// ``` -/// -/// This fragment will not call `foo` every second. -/// Instead, it will call it every second + the time it took to previously call `foo`. -/// -/// Example using ticker, which will consistently call `foo` once a second. -/// -/// ``` no_run -/// # #![feature(type_alias_impl_trait)] -/// # -/// use embassy_executor::time::{Duration, Ticker}; -/// use futures::StreamExt; -/// # fn foo(){} -/// -/// #[embassy_executor::task] -/// async fn ticker_example_1() { -/// let mut ticker = Ticker::every(Duration::from_secs(1)); -/// loop { -/// foo(); -/// ticker.next().await; -/// } -/// } -/// ``` -pub struct Ticker { - expires_at: Instant, - duration: Duration, -} - -impl Ticker { - /// Creates a new ticker that ticks at the specified duration interval. - pub fn every(duration: Duration) -> Self { - let expires_at = Instant::now() + duration; - Self { expires_at, duration } - } -} - -impl Unpin for Ticker {} - -impl Stream for Ticker { - type Item = (); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.expires_at <= Instant::now() { - let dur = self.duration; - self.expires_at += dur; - Poll::Ready(Some(())) - } else { - unsafe { raw::register_timer(self.expires_at, cx.waker()) }; - Poll::Pending - } - } -} -- cgit