diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index c71452398..da3c9a4fc 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -33,7 +33,8 @@ log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } embassy-executor-macros = { version = "0.4.0", path = "../embassy-executor-macros" } -embassy-time = { version = "0.2", path = "../embassy-time", optional = true} +embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true } +embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true } critical-section = "1.1" document-features = "0.2.7" @@ -63,8 +64,8 @@ nightly = ["embassy-executor-macros/nightly"] # See: https://github.com/embassy-rs/embassy/pull/1263 turbowakers = [] -## Use timers from `embassy-time` -integrated-timers = ["dep:embassy-time"] +## Use the executor-integrated `embassy-time` timer queue. +integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"] #! ### Architecture _arch = [] # some arch was picked @@ -177,4 +178,4 @@ task-arena-size-1048576 = [] # END AUTOGENERATED CONFIG FEATURES -#! \ No newline at end of file +#! diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index b16a1c7c3..3f00be4a8 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -30,9 +30,7 @@ use core::ptr::NonNull; use core::task::{Context, Poll}; #[cfg(feature = "integrated-timers")] -use embassy_time::driver::{self, AlarmHandle}; -#[cfg(feature = "integrated-timers")] -use embassy_time::Instant; +use embassy_time_driver::{self, AlarmHandle}; #[cfg(feature = "rtos-trace")] use rtos_trace::trace; @@ -50,7 +48,7 @@ pub(crate) struct TaskHeader { poll_fn: SyncUnsafeCell>, #[cfg(feature = "integrated-timers")] - pub(crate) expires_at: SyncUnsafeCell, + pub(crate) expires_at: SyncUnsafeCell, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -123,7 +121,7 @@ impl TaskStorage { poll_fn: SyncUnsafeCell::new(None), #[cfg(feature = "integrated-timers")] - expires_at: SyncUnsafeCell::new(Instant::from_ticks(0)), + expires_at: SyncUnsafeCell::new(0), #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, @@ -164,7 +162,7 @@ impl TaskStorage { this.raw.state.despawn(); #[cfg(feature = "integrated-timers")] - this.raw.expires_at.set(Instant::MAX); + this.raw.expires_at.set(u64::MAX); } Poll::Pending => {} } @@ -328,7 +326,7 @@ pub(crate) struct SyncExecutor { impl SyncExecutor { pub(crate) fn new(pender: Pender) -> Self { #[cfg(feature = "integrated-timers")] - let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; + let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) }; Self { run_queue: RunQueue::new(), @@ -377,18 +375,19 @@ impl SyncExecutor { /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { #[cfg(feature = "integrated-timers")] - driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); + embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend); + self.timer_queue + .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); self.run_queue.dequeue_all(|p| { let task = p.header(); #[cfg(feature = "integrated-timers")] - task.expires_at.set(Instant::MAX); + task.expires_at.set(u64::MAX); if !task.state.run_dequeue() { // If task is not running, ignore it. This can happen in the following scenario: @@ -418,7 +417,7 @@ impl SyncExecutor { // If this is already in the past, set_alarm might return false // In that case do another poll loop iteration. let next_expiration = self.timer_queue.next_expiration(); - if driver::set_alarm(self.alarm, next_expiration.as_ticks()) { + if embassy_time_driver::set_alarm(self.alarm, next_expiration) { break; } } @@ -568,8 +567,8 @@ pub fn wake_task_no_pend(task: TaskRef) { struct TimerQueue; #[cfg(feature = "integrated-timers")] -impl embassy_time::queue::TimerQueue for TimerQueue { - fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { +impl embassy_time_queue_driver::TimerQueue for TimerQueue { + fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); let task = task.header(); unsafe { @@ -580,7 +579,7 @@ impl embassy_time::queue::TimerQueue for TimerQueue { } #[cfg(feature = "integrated-timers")] -embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); +embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); #[cfg(feature = "rtos-trace")] impl rtos_trace::RtosTraceOSCallbacks for Executor { diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 59a3b43f5..94a5f340b 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,7 +1,5 @@ use core::cmp::min; -use embassy_time::Instant; - use super::TaskRef; use crate::raw::util::SyncUnsafeCell; @@ -30,7 +28,7 @@ impl TimerQueue { pub(crate) unsafe fn update(&self, p: TaskRef) { let task = p.header(); - if task.expires_at.get() != Instant::MAX { + if task.expires_at.get() != u64::MAX { if task.state.timer_enqueue() { task.timer_queue_item.next.set(self.head.get()); self.head.set(Some(p)); @@ -38,18 +36,18 @@ impl TimerQueue { } } - pub(crate) unsafe fn next_expiration(&self) -> Instant { - let mut res = Instant::MAX; + pub(crate) unsafe fn next_expiration(&self) -> u64 { + let mut res = u64::MAX; self.retain(|p| { let task = p.header(); let expires = task.expires_at.get(); res = min(res, expires); - expires != Instant::MAX + expires != u64::MAX }); res } - pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(TaskRef)) { + pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) { self.retain(|p| { let task = p.header(); if task.expires_at.get() <= now { diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md deleted file mode 100644 index d8c0c7d08..000000000 --- a/embassy-time-driver/CHANGELOG.md +++ /dev/null @@ -1,51 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 0.2.0 - 2023-12-04 - -- Added tick rates in multiples of 10 kHz -- Remove nightly and unstable-traits features in preparation for 1.75. -- Update heapless to 0.8. - -## 0.1.5 - 2023-10-16 - -- Added `links` key to Cargo.toml, to prevent multiple copies of this crate in the same binary. - Needed because different copies might get different tick rates, causing - wrong delays if the time driver is using one copy and user code is using another. - This is especially common when mixing crates from crates.io and git. - -## 0.1.4 - 2023-10-12 - -- Added more tick rates - -## 0.1.3 - 2023-08-28 - -- Update `embedded-hal-async` to `1.0.0-rc.2` -- Update `embedded-hal v1` to `1.0.0-rc.2` - -## 0.1.2 - 2023-07-05 - -- Update `embedded-hal-async` to `0.2.0-alpha.2`. -- Update `embedded-hal v1` to `1.0.0-alpha.11`. (Note: v0.2 support is kept unchanged). - -## 0.1.1 - 2023-04-13 - -- Update `embedded-hal-async` to `0.2.0-alpha.1` (uses `async fn` in traits). -- Update `embedded-hal v1` to `1.0.0-alpha.10`. (Note: v0.2 support is kept unchanged). -- Remove dep on `embassy-sync`. -- Fix reentrancy issues in the `std` time driver (#1177) -- Add `Duration::from_hz()`. -- impl `From` conversions to/from `core::time::Duration`. -- Add `#[must_use]` to all futures. -- Add inherent `async fn tick()` to `Ticker`, so you can use it directly without the `Stream` trait. -- Add more tick rates. -- impl `Default` for `Signal` -- Remove unnecessary uses of `atomic-polyfill` - -## 0.1.0 - 2022-08-26 - -- First release diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml new file mode 100644 index 000000000..85ee1da1b --- /dev/null +++ b/embassy-time-queue-driver/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "embassy-time-queue-driver" +version = "0.1.0" +edition = "2021" +description = "Timer queue driver trait for embassy-time" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] + +# Prevent multiple copies of this crate in the same binary. +# Needed because different copies might get different tick rates, causing +# wrong delays if the time driver is using one copy and user code is using another. +# This is especially common when mixing crates from crates.io and git. +links = "embassy-time-queue" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/" +target = "x86_64-unknown-linux-gnu" diff --git a/embassy-time-queue-driver/README.md b/embassy-time-queue-driver/README.md new file mode 100644 index 000000000..8852b0358 --- /dev/null +++ b/embassy-time-queue-driver/README.md @@ -0,0 +1,8 @@ +# embassy-time-queue-driver + +This crate contains the driver trait used by the [`embassy-time`](https://crates.io/crates/embassy-time) timer queue. + +You should rarely need to use this crate directly. Only use it when implementing your own timer queue. + +There is two timer queue implementations, one in `embassy-time` enabled by the `generic-queue` feature, and +another in `embassy-executor` enabled by the `integrated-timers` feature. diff --git a/embassy-time-queue-driver/build.rs b/embassy-time-queue-driver/build.rs new file mode 100644 index 000000000..f328e4d9d --- /dev/null +++ b/embassy-time-queue-driver/build.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/embassy-time/src/queue.rs b/embassy-time-queue-driver/src/lib.rs similarity index 51% rename from embassy-time/src/queue.rs rename to embassy-time-queue-driver/src/lib.rs index d65197c54..50736e8c7 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -1,20 +1,14 @@ -//! Timer queue implementation -//! -//! This module defines the interface a timer queue needs to implement to power the `embassy_time` module. -//! -//! # Implementing a timer queue +#![no_std] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] + +//! ## Implementing a timer queue //! //! - Define a struct `MyTimerQueue` //! - Implement [`TimerQueue`] for it //! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). //! -//! # Linkage details -//! -//! Check the documentation of the [`driver`](crate::driver) module for more information. -//! -//! Similarly to driver, if there is none or multiple timer queues in the crate tree, linking will fail. -//! -//! # Example +//! ## Example //! //! ``` //! use core::task::Waker; @@ -25,23 +19,29 @@ //! struct MyTimerQueue{}; // not public! //! //! impl TimerQueue for MyTimerQueue { -//! fn schedule_wake(&'static self, at: Instant, waker: &Waker) { +//! fn schedule_wake(&'static self, at: u64, waker: &Waker) { //! todo!() //! } //! } -//! ``` -//! ```ignore -//! embassy_time::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); +//! +//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); //! ``` use core::task::Waker; -use crate::Instant; - /// Timer queue pub trait TimerQueue { /// Schedules a waker in the queue to be awoken at moment `at`. /// If this moment is in the past, the waker might be awoken immediately. - fn schedule_wake(&'static self, at: Instant, waker: &Waker); + fn schedule_wake(&'static self, at: u64, waker: &Waker); +} + +extern "Rust" { + fn _embassy_time_schedule_wake(at: u64, waker: &Waker); +} + +/// Schedule the given waker to be woken at `at`. +pub fn schedule_wake(at: u64, waker: &Waker) { + unsafe { _embassy_time_schedule_wake(at, waker) } } /// Set the TimerQueue implementation. @@ -53,8 +53,8 @@ macro_rules! timer_queue_impl { static $name: $t = $val; #[no_mangle] - fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { - <$t as $crate::queue::TimerQueue>::schedule_wake(&$name, at, waker); + fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) { + <$t as $crate::TimerQueue>::schedule_wake(&$name, at, waker); } }; } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 729a2bd4f..f27de8ab4 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -398,6 +398,7 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"] [dependencies] embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } +embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs index 7abc2bd70..8587f9172 100644 --- a/embassy-time/src/driver_mock.rs +++ b/embassy-time/src/driver_mock.rs @@ -1,8 +1,8 @@ use core::cell::RefCell; use critical_section::Mutex as CsMutex; +use embassy_time_driver::{AlarmHandle, Driver}; -use crate::driver::{AlarmHandle, Driver}; use crate::{Duration, Instant}; /// A mock driver that can be manually advanced. @@ -28,7 +28,7 @@ use crate::{Duration, Instant}; /// ``` pub struct MockDriver(CsMutex>); -crate::driver::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new()); +embassy_time_driver::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new()); impl MockDriver { /// Creates a new mock driver. diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 3b5524f9e..d182f8331 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -6,8 +6,7 @@ use std::time::{Duration as StdDuration, Instant as StdInstant}; use std::{mem, ptr, thread}; use critical_section::Mutex as CsMutex; - -use crate::driver::{AlarmHandle, Driver}; +use embassy_time_driver::{AlarmHandle, Driver}; const ALARM_COUNT: usize = 4; @@ -45,7 +44,7 @@ struct TimeDriver { } const ALARM_NEW: AlarmState = AlarmState::new(); -crate::driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { +embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { alarm_count: AtomicU8::new(0), once: Once::new(), diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index d75856c26..ad884f060 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -4,11 +4,10 @@ use std::mem::MaybeUninit; use std::ptr; use std::sync::{Mutex, Once}; +use embassy_time_driver::{AlarmHandle, Driver}; use wasm_bindgen::prelude::*; use wasm_timer::Instant as StdInstant; -use crate::driver::{AlarmHandle, Driver}; - const ALARM_COUNT: usize = 4; struct AlarmState { @@ -42,7 +41,7 @@ struct TimeDriver { } const ALARM_NEW: AlarmState = AlarmState::new(); -crate::driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { +embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { alarm_count: AtomicU8::new(0), once: Once::new(), alarms: UninitCell::uninit(), diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs index 5571cdd15..909f1b173 100644 --- a/embassy-time/src/instant.rs +++ b/embassy-time/src/instant.rs @@ -1,7 +1,7 @@ use core::fmt; use core::ops::{Add, AddAssign, Sub, SubAssign}; -use super::{driver, Duration, GCD_1K, GCD_1M, TICK_HZ}; +use super::{Duration, GCD_1K, GCD_1M, TICK_HZ}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -18,7 +18,9 @@ impl Instant { /// Returns an Instant representing the current time. pub fn now() -> Instant { - Instant { ticks: driver::now() } + Instant { + ticks: embassy_time_driver::now(), + } } /// Create an Instant from a tick count since system boot. diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 3f8c09f1a..d27eb92f6 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -10,12 +10,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub use embassy_time_driver as driver; - mod delay; mod duration; mod instant; -pub mod queue; mod timer; #[cfg(feature = "mock-driver")] @@ -32,8 +29,8 @@ mod driver_wasm; mod queue_generic; pub use delay::{block_for, Delay}; -pub use driver::TICK_HZ; pub use duration::Duration; +pub use embassy_time_driver::TICK_HZ; pub use instant::Instant; pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 829368ffc..cf7a986d5 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -3,10 +3,10 @@ use core::cmp::{min, Ordering}; use core::task::Waker; use critical_section::Mutex; +use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use embassy_time_queue_driver::TimerQueue; use heapless::Vec; -use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; -use crate::queue::TimerQueue; use crate::Instant; #[cfg(feature = "generic-queue-8")] @@ -167,12 +167,12 @@ impl Queue { } impl TimerQueue for Queue { - fn schedule_wake(&'static self, at: Instant, waker: &Waker) { - Queue::schedule_wake(self, at, waker); + fn schedule_wake(&'static self, at: u64, waker: &Waker) { + Queue::schedule_wake(self, Instant::from_ticks(at), waker); } } -crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); +embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new()); #[cfg(test)] #[cfg(feature = "mock-driver")] diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 2705ba03f..565a65cb8 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -1,6 +1,6 @@ use core::future::{poll_fn, Future}; use core::pin::Pin; -use core::task::{Context, Poll, Waker}; +use core::task::{Context, Poll}; use futures_util::future::{select, Either}; use futures_util::stream::FusedStream; @@ -116,7 +116,7 @@ impl Future for Timer { if self.yielded_once && self.expires_at <= Instant::now() { Poll::Ready(()) } else { - schedule_wake(self.expires_at, cx.waker()); + embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); self.yielded_once = true; Poll::Pending } @@ -185,7 +185,7 @@ impl Ticker { self.expires_at += dur; Poll::Ready(()) } else { - schedule_wake(self.expires_at, cx.waker()); + embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); Poll::Pending } }) @@ -202,7 +202,7 @@ impl Stream for Ticker { self.expires_at += dur; Poll::Ready(Some(())) } else { - schedule_wake(self.expires_at, cx.waker()); + embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); Poll::Pending } } @@ -214,11 +214,3 @@ impl FusedStream for Ticker { false } } - -extern "Rust" { - fn _embassy_time_schedule_wake(at: Instant, waker: &Waker); -} - -fn schedule_wake(at: Instant, waker: &Waker) { - unsafe { _embassy_time_schedule_wake(at, waker) } -} diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs index 435089aad..10c85511d 100644 --- a/examples/std/src/bin/serial.rs +++ b/examples/std/src/bin/serial.rs @@ -3,6 +3,7 @@ mod serial_port; use async_io::Async; use embassy_executor::Executor; +use embassy_time as _; use embedded_io_async::Read; use log::*; use nix::sys::termios;