diff options
| author | Dániel Buga <[email protected]> | 2024-12-15 19:24:49 +0100 |
|---|---|---|
| committer | Dániel Buga <[email protected]> | 2024-12-15 19:24:49 +0100 |
| commit | 0492dba5368e7cb22ede2d41d26d4d0431ba2252 (patch) | |
| tree | 735ad32cd3f40acbc57248373ab2622c8a9a03e3 | |
| parent | e861344b179b3e955ac47f1985b7f97fdfb93892 (diff) | |
Update documentation and changelogs
| -rw-r--r-- | embassy-time-driver/CHANGELOG.md | 3 | ||||
| -rw-r--r-- | embassy-time-driver/src/lib.rs | 79 | ||||
| -rw-r--r-- | embassy-time-queue-driver/CHANGELOG.md | 5 | ||||
| -rw-r--r-- | 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 @@ | |||
| 1 | # Changelog for embassy-time-queue-driver | 1 | # Changelog for embassy-time-driver |
| 2 | 2 | ||
| 3 | All notable changes to this project will be documented in this file. | 3 | All notable changes to this project will be documented in this file. |
| 4 | 4 | ||
| @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | ## Unreleased | 8 | ## Unreleased |
| 9 | 9 | ||
| 10 | - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. | 10 | - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. |
| 11 | - `schedule_wake` has been added to the `Driver` trait. | ||
| 11 | 12 | ||
| 12 | ## 0.1.0 - 2024-01-11 | 13 | ## 0.1.0 - 2024-01-11 |
| 13 | 14 | ||
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 @@ | |||
| 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by | 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by |
| 18 | //! enabling a feature on `embassy-time`. | 18 | //! enabling a feature on `embassy-time`. |
| 19 | //! | 19 | //! |
| 20 | //! # Linkage details | 20 | //! ### Example |
| 21 | //! | ||
| 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 23 | //! | ||
| 24 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. | ||
| 25 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 26 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 27 | //! | ||
| 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 29 | //! | ||
| 30 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 31 | //! | ||
| 32 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 33 | //! through generic parameters. This is especially helpful for libraries. | ||
| 34 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 35 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 36 | //! would yield incorrect results. | ||
| 37 | //! | ||
| 38 | //! # Example | ||
| 39 | //! | 21 | //! |
| 40 | //! ``` | 22 | //! ``` |
| 41 | //! use core::task::Waker; | 23 | //! use core::task::Waker; |
| @@ -56,6 +38,65 @@ | |||
| 56 | //! | 38 | //! |
| 57 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | 39 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); |
| 58 | //! ``` | 40 | //! ``` |
| 41 | //! | ||
| 42 | //! ## Implementing the timer queue | ||
| 43 | //! | ||
| 44 | //! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the | ||
| 45 | //! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`). | ||
| 46 | //! | ||
| 47 | //! Then, you'll need to adapt the `schedule_wake` method to use this queue. | ||
| 48 | //! | ||
| 49 | //! ```ignore | ||
| 50 | //! use core::cell::RefCell; | ||
| 51 | //! use core::task::Waker; | ||
| 52 | //! | ||
| 53 | //! use embassy_time_queue_driver::Queue; | ||
| 54 | //! use embassy_time_driver::Driver; | ||
| 55 | //! | ||
| 56 | //! struct MyDriver { | ||
| 57 | //! timer_queue: critical_section::Mutex<RefCell<Queue>>, | ||
| 58 | //! } | ||
| 59 | //! | ||
| 60 | //! impl MyDriver { | ||
| 61 | //! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool { | ||
| 62 | //! todo!() | ||
| 63 | //! } | ||
| 64 | //! } | ||
| 65 | //! | ||
| 66 | //! impl Driver for MyDriver { | ||
| 67 | //! // fn now(&self) -> u64 { ... } | ||
| 68 | //! | ||
| 69 | //! fn schedule_wake(&self, at: u64, waker: &Waker) { | ||
| 70 | //! critical_section::with(|cs| { | ||
| 71 | //! let mut queue = self.queue.borrow(cs).borrow_mut(); | ||
| 72 | //! if queue.schedule_wake(at, waker) { | ||
| 73 | //! let mut next = queue.next_expiration(self.now()); | ||
| 74 | //! while !self.set_alarm(cs, next) { | ||
| 75 | //! next = queue.next_expiration(self.now()); | ||
| 76 | //! } | ||
| 77 | //! } | ||
| 78 | //! }); | ||
| 79 | //! } | ||
| 80 | //! } | ||
| 81 | //! ``` | ||
| 82 | //! | ||
| 83 | //! # Linkage details | ||
| 84 | //! | ||
| 85 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 86 | //! | ||
| 87 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. | ||
| 88 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 89 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 90 | //! | ||
| 91 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 92 | //! | ||
| 93 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 94 | //! | ||
| 95 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 96 | //! through generic parameters. This is especially helpful for libraries. | ||
| 97 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 98 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 99 | //! would yield incorrect results. | ||
| 59 | 100 | ||
| 60 | //! ## Feature flags | 101 | //! ## Feature flags |
| 61 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] | 102 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] |
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 | |||
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | ## Unreleased |
| 9 | 9 | ||
| 10 | - Added `integrated-timers` and `generic-queue-N` features | 10 | - Added `generic-queue-N` features. |
| 11 | - Added `queue_generic` module which contains `Queue` (configured via the `generic-queue-N` features) and `ConstGenericQueue<SIZE>`. | 11 | - Added `embassy_time_queue_driver::Queue` struct which uses integrated or a generic storage (configured using `generic-queue-N`). |
| 12 | - Added `GenericTimerQueue` and `GlobalTimerQueue` structs that can be used to implement timer queues. | ||
| 13 | 12 | ||
| 14 | ## 0.1.0 - 2024-01-11 | 13 | ## 0.1.0 - 2024-01-11 |
| 15 | 14 | ||
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 @@ | |||
| 2 | #![doc = include_str!("../README.md")] | 2 | #![doc = include_str!("../README.md")] |
| 3 | #![warn(missing_docs)] | 3 | #![warn(missing_docs)] |
| 4 | 4 | ||
| 5 | //! ## Implementing a timer queue | 5 | //! This crate is an implementation detail of `embassy-time-driver`. |
| 6 | //! | 6 | //! |
| 7 | //! - Define a struct `MyTimerQueue` | 7 | //! As a HAL user, you should only depend on this crate if your application does not use |
| 8 | //! - Implement [`TimerQueue`] for it | 8 | //! `embassy-executor` and your HAL does not configure a generic queue by itself. |
| 9 | //! - Register it as the global timer queue with [`timer_queue_impl`]. | ||
| 10 | //! - Ensure that you process the timer queue when `schedule_wake` is due. This usually involves | ||
| 11 | //! waking expired tasks, finding the next expiration time and setting an alarm. | ||
| 12 | //! | 9 | //! |
| 13 | //! If a single global timer queue is sufficient for you, you can use the | 10 | //! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, |
| 14 | //! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue | 11 | //! but how you should do so is documented in [`embassy_time_driver`]. |
| 15 | //! protected by a critical section. | ||
| 16 | //! | ||
| 17 | //! ``` | ||
| 18 | //! use embassy_time_queue_driver::GlobalTimerQueue; | ||
| 19 | //! embassy_time_queue_driver::timer_queue_impl!( | ||
| 20 | //! static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 21 | //! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm")) | ||
| 22 | //! ); | ||
| 23 | //! ``` | ||
| 24 | //! | ||
| 25 | //! You can also use the `queue_generic` or the `queue_integrated` modules to implement your own | ||
| 26 | //! timer queue. These modules contain queue implementations which you can wrap and tailor to | ||
| 27 | //! your needs. | ||
| 28 | //! | ||
| 29 | //! If you are providing an embassy-executor implementation besides a timer queue, you can choose to | ||
| 30 | //! expose the `integrated-timers` feature in your implementation. This feature stores timer items | ||
| 31 | //! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation. | ||
| 32 | //! | ||
| 33 | //! ## Example | ||
| 34 | //! | ||
| 35 | //! ``` | ||
| 36 | //! use core::task::Waker; | ||
| 37 | //! | ||
| 38 | //! use embassy_time::Instant; | ||
| 39 | //! use embassy_time::queue::TimerQueue; | ||
| 40 | //! | ||
| 41 | //! struct MyTimerQueue{}; // not public! | ||
| 42 | //! | ||
| 43 | //! impl TimerQueue for MyTimerQueue { | ||
| 44 | //! fn schedule_wake(&'static self, at: u64, waker: &Waker) { | ||
| 45 | //! todo!() | ||
| 46 | //! } | ||
| 47 | //! } | ||
| 48 | //! | ||
| 49 | //! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); | ||
| 50 | //! ``` | ||
| 51 | 12 | ||
| 52 | use core::task::Waker; | 13 | use core::task::Waker; |
| 53 | 14 | ||
