diff options
Diffstat (limited to 'pw_chrono/docs.rst')
-rw-r--r-- | pw_chrono/docs.rst | 453 |
1 files changed, 178 insertions, 275 deletions
diff --git a/pw_chrono/docs.rst b/pw_chrono/docs.rst index 4495b8713..776b5cd12 100644 --- a/pw_chrono/docs.rst +++ b/pw_chrono/docs.rst @@ -8,7 +8,7 @@ leveraging many pieces of STL's the ``std::chrono`` library but with a focus on portability for constrained embedded devices and maintaining correctness. .. note:: - This module is still under construction, the API is not yet stable. + This module is still under construction, the API is not yet stable. ------------------------------- ``duration`` and ``time_point`` @@ -47,41 +47,41 @@ following simplified model, ignoring most of their member functions: .. code-block:: cpp - namespace std::chrono { + namespace std::chrono { - template<class Rep, class Period = std::ratio<1, 1>> - class duration { - public: - using rep = Rep; - using period = Period; + template<class Rep, class Period = std::ratio<1, 1>> + class duration { + public: + using rep = Rep; + using period = Period; - constexpr rep count() const { return tick_count_; } + constexpr rep count() const { return tick_count_; } - static constexpr duration zero() noexcept { - return duration(0); - } + static constexpr duration zero() noexcept { + return duration(0); + } - // Other member functions... + // Other member functions... - private: - rep tick_count_; - }; + private: + rep tick_count_; + }; - template<class Clock, class Duration = typename Clock::duration> - class time_point { - public: - using duration = Duration; - using rep = Duration::rep; - using period = Duration::period; - using clock = Clock; + template<class Clock, class Duration = typename Clock::duration> + class time_point { + public: + using duration = Duration; + using rep = Duration::rep; + using period = Duration::period; + using clock = Clock; - constexpr duration time_since_epoch() const { return time_since_epoch_; } + constexpr duration time_since_epoch() const { return time_since_epoch_; } - // Other member functions... + // Other member functions... - private: - duration time_since_epoch_; - }; + private: + duration time_since_epoch_; + }; } // namespace std::chrono @@ -107,11 +107,11 @@ With this guidance one can avoid common pitfalls like ``uint32_t`` millisecond tick rollover bugs when using RTOSes every 49.7 days. .. warning:: - We recommend avoiding the ``duration<>::min()`` and ``duration<>::max()`` - helper member functions where possible as they exceed the ±292 years duration - limit assumption. There's an immediate risk of integer underflow or overflow - for any arithmetic operations. Consider using ``std::optional`` instead of - priming a variable with a value at the limit. + We recommend avoiding the ``duration<>::min()`` and ``duration<>::max()`` + helper member functions where possible as they exceed the ±292 years duration + limit assumption. There's an immediate risk of integer underflow or overflow + for any arithmetic operations. Consider using ``std::optional`` instead of + priming a variable with a value at the limit. Helper duration types and literals ================================== @@ -129,11 +129,11 @@ As an example you can use these as follows: .. code-block:: cpp - #include <chrono> + #include <chrono> - void Foo() { - Bar(std::chrono::milliseconds(42)); - } + void Foo() { + Bar(std::chrono::milliseconds(42)); + } In addition, the inline namespace ``std::literals::chrono_literals`` includes: @@ -148,12 +148,12 @@ As an example you can use these as follows: .. code-block:: cpp - using std::literals::chrono_literals::ms; - // Or if you want them all: using namespace std::chrono_literals; + using std::literals::chrono_literals::ms; + // Or if you want them all: using namespace std::chrono_literals; - void Foo() { - Bar(42ms); - } + void Foo() { + Bar(42ms); + } For these helper duration types to be compatible with API's that take a `SystemClock::duration` either an :ref:`implicit<Implicit lossless conversions>` @@ -167,29 +167,29 @@ a 1kHz RTOS tick period and you would like to express a timeout duration: .. code-block:: cpp - // Instead of using ticks which are not portable between RTOS configurations, - // as the tick period may be different: - constexpr uint32_t kFooNotificationTimeoutTicks = 42; - bool TryGetNotificationFor(uint32_t ticks); - - // And instead of using a time unit which is prone to accidental conversion - // errors as all variables must maintain the time units: - constexpr uint32_t kFooNotificationTimeoutMs = 42; - bool TryGetNotificationFor(uint32_t milliseconds); - - // We can instead use a defined clock and its duration for the kernel and rely - // on implicit lossless conversions: - #include <chrono> - #include "pw_chrono/system_clock.h" - constexpr SystemClock::duration kFooNotificationTimeout = - std::chrono::milliseconds(42); - bool TryGetNotificationFor(SystemClock::duration timeout); - - void MaybeProcessNotification() { - if (TryGetNotificationFor(kFooNotificationTimeout)) { - ProcessNotification(); - } - } + // Instead of using ticks which are not portable between RTOS configurations, + // as the tick period may be different: + constexpr uint32_t kFooNotificationTimeoutTicks = 42; + bool TryGetNotificationFor(uint32_t ticks); + + // And instead of using a time unit which is prone to accidental conversion + // errors as all variables must maintain the time units: + constexpr uint32_t kFooNotificationTimeoutMs = 42; + bool TryGetNotificationFor(uint32_t milliseconds); + + // We can instead use a defined clock and its duration for the kernel and rely + // on implicit lossless conversions: + #include <chrono> + #include "pw_chrono/system_clock.h" + constexpr SystemClock::duration kFooNotificationTimeout = + std::chrono::milliseconds(42); + bool TryGetNotificationFor(SystemClock::duration timeout); + + void MaybeProcessNotification() { + if (TryGetNotificationFor(kFooNotificationTimeout)) { + ProcessNotification(); + } + } .. _Implicit lossless conversions: @@ -213,15 +213,15 @@ some values like ``0``, ``1000``, etc. .. code-block:: cpp - #include <chrono> + #include <chrono> - constexpr std::chrono::milliseconds this_compiles = - std::chrono::seconds(42); + constexpr std::chrono::milliseconds this_compiles = + std::chrono::seconds(42); - // This cannot compile, because for some duration values it is lossy even - // though this particular value can be in theory converted to whole seconds. - // constexpr std::chrono::seconds this_does_not_compile = - // std::chrono::milliseconds(1000); + // This cannot compile, because for some duration values it is lossy even + // though this particular value can be in theory converted to whole seconds. + // constexpr std::chrono::seconds this_does_not_compile = + // std::chrono::milliseconds(1000); .. _Explicit lossy conversions: @@ -246,41 +246,41 @@ recommends explicitly using: as a more explicit form of std::chrono::ceil. .. Note:: - Pigweed does not recommend using ``std::chrono::duration_cast<>`` which - truncates dowards zero like ``static_cast``. This is typically not the desired - rounding behavior when dealing with time units. Instead, where possible we - recommend the more explicit, self-documenting ``std::chrono::floor``, - ``std::chrono::round``, and ``std::chrono::ceil``. + Pigweed does not recommend using ``std::chrono::duration_cast<>`` which + truncates dowards zero like ``static_cast``. This is typically not the desired + rounding behavior when dealing with time units. Instead, where possible we + recommend the more explicit, self-documenting ``std::chrono::floor``, + ``std::chrono::round``, and ``std::chrono::ceil``. Now knowing this, the previous example could be portably and correctly handled as follows: .. code-block:: cpp - #include <chrono> + #include <chrono> - #include "pw_chrono/system_clock.h" + #include "pw_chrono/system_clock.h" - // We want to round up to ensure we block for at least the specified duration, - // instead of rounding down. Imagine for example the extreme case where you - // may round down to zero or one, you would definitely want to at least block. - constexpr SystemClock::duration kFooNotificationTimeout = - std::chrono::ceil(std::chrono::milliseconds(42)); - bool TryGetNotificationFor(SystemClock::duration timeout); + // We want to round up to ensure we block for at least the specified duration, + // instead of rounding down. Imagine for example the extreme case where you + // may round down to zero or one, you would definitely want to at least block. + constexpr SystemClock::duration kFooNotificationTimeout = + std::chrono::ceil(std::chrono::milliseconds(42)); + bool TryGetNotificationFor(SystemClock::duration timeout); - void MaybeProcessNotification() { - if (TryGetNotificationFor(kFooNotificationTimeout)) { - ProcessNotification(); - } - } + void MaybeProcessNotification() { + if (TryGetNotificationFor(kFooNotificationTimeout)) { + ProcessNotification(); + } + } This code is lossless if the clock period is 1kHz and it's correct using a division which rounds up when the clock period is 128Hz. .. Note:: - When using ``pw::chrono::SystemClock::duration`` for timeouts we recommend - using its ``SystemClock::for_at_least()`` to round up timeouts in a more - explicit, self documenting manner which uses ``std::chrono::ceil`` internally. + When using ``pw::chrono::SystemClock::duration`` for timeouts we recommend + using its ``SystemClock::for_at_least()`` to round up timeouts in a more + explicit, self documenting manner which uses ``std::chrono::ceil`` internally. Use of ``count()`` and ``time_since_epoch()`` ============================================= @@ -317,10 +317,10 @@ This same risk exists if a continuously running hardware timer is used for a software timer service. .. Note:: - When calculating deadlines based on a timeout when using - ``pw::chrono::SystemClock::timeout``, we recommend using its - ``SystemClock::TimePointAfterAtLeast()`` which adds an extra tick for you - internally. + When calculating deadlines based on a timeout when using + ``pw::chrono::SystemClock::timeout``, we recommend using its + ``SystemClock::TimePointAfterAtLeast()`` which adds an extra tick for you + internally. ------ Clocks @@ -425,91 +425,44 @@ scheduler in Pigweed including pw_sync, pw_thread, etc. C++ --- - -.. cpp:class:: pw::chrono::SystemClock - - .. cpp:type:: rep = int64_t; - - .. cpp:type:: period = std::ratio<PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMBERATOR, PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR>; - - The period is specified by the backend. - - .. cpp:type:: duration = std::chrono::duration<rep, period>; - - .. cpp:type:: time_point = std::chrono::time_point<SystemClock>; - - .. cpp:member:: static constexpr Epoch epoch = backend::kSystemClockEpoch; - - The epoch must be provided by the backend. - - .. cpp:member:: static constexpr bool is_monotonic = true; - - The time points of this clock cannot decrease. - - .. cpp:member:: static constexpr bool is_steady = false; - - However, the time between ticks of this clock may slightly vary due to sleep - modes. The duration during sleep may be ignored or backfilled with another - clock. - - .. cpp:member:: static constexpr bool is_free_running = backend::kSystemClockFreeRunning; - - The now() function may not move forward while in a critical section or - interrupt. This must be provided by the backend. - - .. cpp:member:: static constexpr bool is_stopped_in_halting_debug_mode = true; - - The clock must stop while in halting debug mode. - - .. cpp:member:: static constexpr bool is_always_enabled = true; - - The now() function can be invoked at any time. - - .. cpp:member:: static constexpr bool is_nmi_safe = backend::kSystemClockNmiSafe; - - The now() function may work in non-masking interrupts, depending on the - backend. This must be provided by the backend. - - .. cpp:function:: static time_point now() noexcept; - - This is thread and IRQ safe. - - .. cpp:function:: template <class Rep, class Period> static constexpr duration for_at_least(std::chrono::duration<Rep, Period> d); - - This is purely a helper, identical to directly using std::chrono::ceil, to - convert a duration type which cannot be implicitly converted where the - result is rounded up. - - .. cpp:function:: static time_point TimePointAfterAtLeast(duration after_at_least); - - Computes the nearest time_point after the specified duration has elapsed. - - This is useful for translating delay or timeout durations into deadlines. - - The time_point is computed based on now() plus the specified duration - where a singular clock tick is added to handle partial ticks. This ensures - that a duration of at least 1 tick does not result in [0,1] ticks and - instead in [1,2] ticks. - +.. doxygenstruct:: pw::chrono::SystemClock + :members: Example in C++ -------------- - .. code-block:: cpp - #include <chrono> - - #include "pw_chrono/system_clock.h" - - void Foo() { - const SystemClock::time_point before = SystemClock::now(); - TakesALongTime(); - const SystemClock::duration time_taken = SystemClock::now() - before; - bool took_way_too_long = false; - if (time_taken > std::chrono::seconds(42)) { - took_way_too_long = true; - } - } + #include <chrono> + + #include "pw_chrono/system_clock.h" + + void Foo() { + const SystemClock::time_point before = SystemClock::now(); + TakesALongTime(); + const SystemClock::duration time_taken = SystemClock::now() - before; + bool took_way_too_long = false; + if (time_taken > std::chrono::seconds(42)) { + took_way_too_long = true; + } + } + +Protobuf +======== +Sometimes it's desirable to communicate high resolution time points and +durations from one device to another. For this, ``pw_chrono`` provides protobuf +representations of clock parameters (``pw.chrono.ClockParameters``) and time +points (``pw.chrono.TimePoint``). These types are less succinct than simple +single-purpose fields like ``ms_since_boot`` or ``unix_timestamp``, but allow +timestamps to be communicated in terms of the tick rate of a device, potentially +providing significantly higher resolution. Logging, tracing, and system state +snapshots are use cases that benefit from this additional resolution. + +This module provides an overlay proto (``pw.chrono.SnapshotTimestamps``) for +usage with ``pw_snapshot`` to encourage capture of high resolution timestamps +in device snapshots. Simplified capture utilies and host-side tooling to +interpret this data are not yet provided by ``pw_chrono``. + +There is tooling that take these proto and make them more human readable. --------------- Software Timers @@ -535,116 +488,66 @@ The ExpiryCallback is either invoked from a high priority thread or an interrupt. Ergo ExpiryCallbacks should be treated as if they are executed by an interrupt, meaning: - * Processing inside of the callback should be kept to a minimum. +* Processing inside of the callback should be kept to a minimum. - * Callbacks should never attempt to block. +* Callbacks should never attempt to block. - * APIs which are not interrupt safe such as pw::sync::Mutex should not be used! +* APIs which are not interrupt safe such as pw::sync::Mutex should not be used! C++ --- -.. cpp:class:: pw::chrono::SystemTimer - - .. cpp:function:: SystemTimer(ExpiryCallback callback) - - Constructs the SystemTimer based on the user provided - ``pw::Function<void(SystemClock::time_point expired_deadline)>``. Note that - The ExpiryCallback is either invoked from a high priority thread or an - interrupt. - - .. note:: - For a given timer instance, its ExpiryCallback will not preempt itself. - This makes it appear like there is a single executor of a timer instance's - ExpiryCallback. - - .. cpp:function:: ~SystemTimer() - - Cancels the timer and blocks if necssary if the callback is already being - processed. - - **Postcondition:** The expiry callback is not in progress and will not be - called in the future. - - .. cpp:function:: void InvokeAfter(chrono::SystemClock::duration delay) - - Invokes the expiry callback as soon as possible after at least the - specified duration. - - Scheduling a callback cancels the existing callback (if pending). - If the callback is already being executed while you reschedule it, it will - finish callback execution to completion. You are responsible for any - critical section locks which may be needed for timer coordination. - - This is thread safe, it may not be IRQ safe. - - .. cpp:function:: void InvokeAt(chrono::SystemClock::time_point timestamp) - - Invokes the expiry callback as soon as possible starting at the specified - time_point. - - Scheduling a callback cancels the existing callback (if pending). - If the callback is already being executed while you reschedule it, it will - finish callback execution to completion. You are responsible for any - critical section locks which may be needed for timer coordination. - - This is thread safe, it may not be IRQ safe. - - .. cpp:function:: void Cancel() - - Cancels the software timer expiry callback if pending. - - Canceling a timer which isn't scheduled does nothing. - - If the callback is already being executed while you cancel it, it will - finish callback execution to completion. You are responsible for any - synchronization which is needed for thread safety. - - This is thread safe, it may not be IRQ safe. - - .. list-table:: - - * - *Safe to use in context* - - *Thread* - - *Interrupt* - - *NMI* - * - ``SystemTimer::SystemTimer`` - - ✔ - - - - - * - ``SystemTimer::~SystemTimer`` - - ✔ - - - - - * - ``void SystemTimer::InvokeAfter`` - - ✔ - - - - - * - ``void SystemTimer::InvokeAt`` - - ✔ - - - - - * - ``void SystemTimer::Cancel`` - - ✔ - - - - +.. doxygenclass:: pw::chrono::SystemTimer + :members: + +.. cpp:namespace-push:: pw::chrono::SystemTimer + +.. list-table:: + :widths: 70 10 10 10 + :header-rows: 1 + + * - Safe to use in context + - Thread + - Interrupt + - NMI + * - :cpp:func:`pw::chrono::SystemTimer::SystemTimer` + - ✔ + - + - + * - :cpp:func:`pw::chrono::SystemTimer::~SystemTimer` + - ✔ + - + - + * - :cpp:func:`InvokeAfter` + - ✔ + - + - + * - :cpp:func:`InvokeAt` + - ✔ + - + - + * - :cpp:func:`Cancel` + - ✔ + - + - + +.. cpp:namespace-pop:: Example in C++ -------------- - .. code-block:: cpp - #include "pw_chrono/system_clock.h" - #include "pw_chrono/system_timer.h" - #include "pw_log/log.h" + #include "pw_chrono/system_clock.h" + #include "pw_chrono/system_timer.h" + #include "pw_log/log.h" - using namespace std::chrono_literals; + using namespace std::chrono_literals; - void DoFoo(pw::chrono::SystemClock::time_point expired_deadline) { - PW_LOG_INFO("Timer callback invoked!"); - } + void DoFoo(pw::chrono::SystemClock::time_point expired_deadline) { + PW_LOG_INFO("Timer callback invoked!"); + } - pw::chrono::SystemTimer foo_timer(DoFoo); + pw::chrono::SystemTimer foo_timer(DoFoo); - void DoFooLater() { - foo_timer.InvokeAfter(42ms); // DoFoo will be invoked after 42ms. - } + void DoFooLater() { + foo_timer.InvokeAfter(42ms); // DoFoo will be invoked after 42ms. + } |