From 0492dba5368e7cb22ede2d41d26d4d0431ba2252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 15 Dec 2024 19:24:49 +0100 Subject: [PATCH] Update documentation and changelogs --- embassy-time-driver/CHANGELOG.md | 3 +- embassy-time-driver/src/lib.rs | 79 +++++++++++++++++++------- embassy-time-queue-driver/CHANGELOG.md | 5 +- embassy-time-queue-driver/src/lib.rs | 49 ++-------------- 4 files changed, 69 insertions(+), 67 deletions(-) diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index ebc37b6f4..2af1dc736 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md @@ -1,4 +1,4 @@ -# Changelog for embassy-time-queue-driver +# Changelog for embassy-time-driver All notable changes to this project will be documented in this file. @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. +- `schedule_wake` has been added to the `Driver` trait. ## 0.1.0 - 2024-01-11 diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 090969d8c..57a9f7587 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -17,25 +17,7 @@ //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by //! enabling a feature on `embassy-time`. //! -//! # 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 function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. -//! The driver crate defines the function 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 +//! ### Example //! //! ``` //! use core::task::Waker; @@ -56,6 +38,65 @@ //! //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); //! ``` +//! +//! ## Implementing the timer queue +//! +//! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the +//! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`). +//! +//! Then, you'll need to adapt the `schedule_wake` method to use this queue. +//! +//! ```ignore +//! use core::cell::RefCell; +//! use core::task::Waker; +//! +//! use embassy_time_queue_driver::Queue; +//! use embassy_time_driver::Driver; +//! +//! struct MyDriver { +//! timer_queue: critical_section::Mutex>, +//! } +//! +//! impl MyDriver { +//! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool { +//! todo!() +//! } +//! } +//! +//! impl Driver for MyDriver { +//! // fn now(&self) -> u64 { ... } +//! +//! fn schedule_wake(&self, at: u64, waker: &Waker) { +//! critical_section::with(|cs| { +//! let mut queue = self.queue.borrow(cs).borrow_mut(); +//! if queue.schedule_wake(at, waker) { +//! let mut next = queue.next_expiration(self.now()); +//! while !self.set_alarm(cs, next) { +//! next = queue.next_expiration(self.now()); +//! } +//! } +//! }); +//! } +//! } +//! ``` +//! +//! # 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 function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. +//! The driver crate defines the function 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. //! ## Feature flags #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] diff --git a/embassy-time-queue-driver/CHANGELOG.md b/embassy-time-queue-driver/CHANGELOG.md index 3b2aa8695..a99f250ed 100644 --- a/embassy-time-queue-driver/CHANGELOG.md +++ b/embassy-time-queue-driver/CHANGELOG.md @@ -7,9 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Added `integrated-timers` and `generic-queue-N` features -- Added `queue_generic` module which contains `Queue` (configured via the `generic-queue-N` features) and `ConstGenericQueue`. -- Added `GenericTimerQueue` and `GlobalTimerQueue` structs that can be used to implement timer queues. +- Added `generic-queue-N` features. +- Added `embassy_time_queue_driver::Queue` struct which uses integrated or a generic storage (configured using `generic-queue-N`). ## 0.1.0 - 2024-01-11 diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 46dd646ca..d8b01df3b 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -2,52 +2,13 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] -//! ## Implementing a timer queue +//! This crate is an implementation detail of `embassy-time-driver`. //! -//! - Define a struct `MyTimerQueue` -//! - Implement [`TimerQueue`] for it -//! - Register it as the global timer queue with [`timer_queue_impl`]. -//! - Ensure that you process the timer queue when `schedule_wake` is due. This usually involves -//! waking expired tasks, finding the next expiration time and setting an alarm. +//! As a HAL user, you should only depend on this crate if your application does not use +//! `embassy-executor` and your HAL does not configure a generic queue by itself. //! -//! If a single global timer queue is sufficient for you, you can use the -//! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue -//! protected by a critical section. -//! -//! ``` -//! use embassy_time_queue_driver::GlobalTimerQueue; -//! embassy_time_queue_driver::timer_queue_impl!( -//! static TIMER_QUEUE_DRIVER: GlobalTimerQueue -//! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm")) -//! ); -//! ``` -//! -//! You can also use the `queue_generic` or the `queue_integrated` modules to implement your own -//! timer queue. These modules contain queue implementations which you can wrap and tailor to -//! your needs. -//! -//! If you are providing an embassy-executor implementation besides a timer queue, you can choose to -//! expose the `integrated-timers` feature in your implementation. This feature stores timer items -//! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation. -//! -//! ## Example -//! -//! ``` -//! use core::task::Waker; -//! -//! use embassy_time::Instant; -//! use embassy_time::queue::TimerQueue; -//! -//! struct MyTimerQueue{}; // not public! -//! -//! impl TimerQueue for MyTimerQueue { -//! fn schedule_wake(&'static self, at: u64, waker: &Waker) { -//! todo!() -//! } -//! } -//! -//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); -//! ``` +//! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, +//! but how you should do so is documented in [`embassy_time_driver`]. use core::task::Waker;