diff --git a/book/en/src/by-example/timer-queue.md b/book/en/src/by-example/timer-queue.md index 167939cecf..9613ce1dbd 100644 --- a/book/en/src/by-example/timer-queue.md +++ b/book/en/src/by-example/timer-queue.md @@ -95,3 +95,32 @@ Running the program on real hardware produces the following output in the consol ``` text {{#include ../../../../ci/expected/baseline.run}} ``` + +## Caveats + +The `Instant` and `Duration` APIs are meant to be exclusively used with the +`schedule` API to schedule tasks *with a precision of a single core clock +cycle*. These APIs are *not*, for example, meant to be used to time external +events like a user pressing a button. + +The timer queue feature internally uses the system timer, `SysTick`. This timer +is a 24-bit counter and it's clocked at the core clock frequency so tasks +scheduled more than `(1 << 24).cycles()` in the future will incur in additional +overhead, proportional to the size of their `Duration`, compared to task +scheduled with `Duration`s below that threshold. + +If you need periodic tasks with periods greater than `(1 << 24).cycles()` you +likely don't need a timer with a resolution of one core clock cycle so we advise +you instead use a device timer with an appropriate prescaler. + +We can't stop you from using `Instant` to measure external events so please be +aware that `Instant.sub` / `Instant.elapsed` will never return a `Duration` +equal or greater than `(1 << 31).cycles()` so you won't be able to measure +events that last more than `1 << 31` core clock cycles (e.g. seconds). + +Adding a `Duration` equal or greater than `(1 << 31).cycles()` to an `Instant` +will effectively overflow it so it's not possible to schedule a task more than +`(1 << 31).cycles()` in the future. There are some debug assertions in place to +catch this kind of user error but it's not possible to prevent it with 100% +success rate because one can always write `(instant + duration) + duration` and +bypass the runtime checks. diff --git a/src/lib.rs b/src/lib.rs index b0bf6689e3..991e77dcb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,16 @@ pub struct Peripherals<'a> { /// A measurement of a monotonically nondecreasing clock. Opaque and useful only with `Duration` /// /// This data type is only available when the `timer-queue` feature is enabled +/// +/// **NOTE:** Both `Instant` and `Duration` are only meant to be used with the `schedule` API. +/// +/// We can't stop you from using `Instant.sub` to measure things so please be aware that you won't +/// be able to measure events that span longer than `(1 << 31) - 1` core clock cycles. +/// +/// Also note that adding a duration equal or greater than `(1 << 31).cycles()` to an `Instant` will +/// effectively overflow it. There's a debug assertion in place to prevent this user error but it's +/// not possible to catch this error with 100% success rate because one can write `(instant + +/// duration) + duration` to bypass runtime checks. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg(feature = "timer-queue")] pub struct Instant(i32); @@ -217,6 +227,17 @@ impl PartialOrd for Instant { /// A `Duration` type to represent a span of time. /// /// This data type is only available when the `timer-queue` feature is enabled +/// +/// **NOTE:** Both `Instant` and `Duration` are only meant to be used with the `schedule` API. +/// +/// `Duration` has a resolution of 1 core clock cycle and an effective (half-open) range of `0..(1 +/// << 31)` (end not included) *core clock cycles*. +/// +/// Be aware that scheduling a (periodic) task more than `(1 << 24).cycles()` in the future will +/// incur in additional overhead proportional to the value of the `Duration`. If you need periodic +/// tasks with periods greater than `1 << 24` (e.g. with periods in seconds) you likely don't a +/// resolution of one core clock cycle so we suggest you use a hardware timer with an appropriate +/// prescaler. #[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] #[cfg(feature = "timer-queue")] pub struct Duration(u32);