time: split queue driver too, don't reexport drivers.

This commit is contained in:
Dario Nieuwenhuis 2024-01-11 22:47:05 +01:00
parent b3ab2d91f7
commit f0606da9ad
17 changed files with 100 additions and 128 deletions

View file

@ -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
#! </details>
#! </details>

View file

@ -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<Option<unsafe fn(TaskRef)>>,
#[cfg(feature = "integrated-timers")]
pub(crate) expires_at: SyncUnsafeCell<Instant>,
pub(crate) expires_at: SyncUnsafeCell<u64>,
#[cfg(feature = "integrated-timers")]
pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
}
@ -123,7 +121,7 @@ impl<F: Future + 'static> TaskStorage<F> {
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<F: Future + 'static> TaskStorage<F> {
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 {

View file

@ -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 {

View file

@ -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

View file

@ -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"

View file

@ -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.

View file

@ -0,0 +1 @@
fn main() {}

View file

@ -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);
}
};
}

View file

@ -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 }

View file

@ -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<RefCell<InnerMockDriver>>);
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.

View file

@ -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(),

View file

@ -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(),

View file

@ -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.

View file

@ -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};

View file

@ -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")]

View file

@ -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) }
}

View file

@ -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;