Update documentation and changelogs

This commit is contained in:
Dániel Buga 2024-12-15 19:24:49 +01:00
parent e861344b17
commit 0492dba536
No known key found for this signature in database
4 changed files with 69 additions and 67 deletions

View File

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

View File

@ -17,25 +17,7 @@
//! Otherwise, dont 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<RefCell<Queue>>,
//! }
//!
//! 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#"<span class="stab portability"><code>{feature}</code></span>"#)]

View File

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

View File

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