xref: /aosp_15_r20/external/pigweed/pw_chrono/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_chrono:
2
3=========
4pw_chrono
5=========
6.. pigweed-module::
7   :name: pw_chrono
8
9Pigweed's chrono module provides facilities for applications to deal with time,
10leveraging many pieces of STL's the ``std::chrono`` library but with a focus
11on portability for constrained embedded devices and maintaining correctness.
12
13-------------------------------
14``duration`` and ``time_point``
15-------------------------------
16Pigweed's time primitives rely on C++'s
17`<chrono> <https://en.cppreference.com/w/cpp/header/chrono>`_ library to enable
18users to express intents with strongly typed real time units through
19`std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_
20and
21`std::chrono::time_point <https://en.cppreference.com/w/cpp/chrono/time_point>`_
22.
23
24What are they?
25==============
26At a high level, durations and time_points at run time are tick counts which
27are wrapped in templated metadata which is only used at compile time.
28
29The STL's
30`std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_
31class template represents a time interval. It consists of a count of ticks of
32type ``rep`` and a tick ``period``, where the tick period is a ``std::ratio``
33representing the time in seconds from one tick to the next.
34
35The only data stored in a duration is a tick count of type ``rep``. The
36``period`` is included as part of the duration's type, and is only used when
37converting between different durations.
38
39Similarly, the STL's
40`std::chrono::time_point <https://en.cppreference.com/w/cpp/chrono/time_point>`_
41class template represents a point in time (i.e. timestamp). It consists of a
42value of type ``duration`` which represents the time interval from the start of
43the ``clock``'s epoch.
44
45The ``duration`` and ``time_point`` class templates can be represented with the
46following simplified model, ignoring most of their member functions:
47
48.. code-block:: cpp
49
50   namespace std::chrono {
51
52   template<class Rep, class Period = std::ratio<1, 1>>
53   class duration {
54    public:
55     using rep = Rep;
56     using period = Period;
57
58     constexpr rep count() const { return tick_count_; }
59
60     static constexpr duration zero() noexcept {
61       return duration(0);
62     }
63
64     // Other member functions...
65
66    private:
67     rep tick_count_;
68   };
69
70   template<class Clock, class Duration = typename Clock::duration>
71   class time_point {
72    public:
73     using duration = Duration;
74     using rep = Duration::rep;
75     using period = Duration::period;
76     using clock = Clock;
77
78     constexpr duration time_since_epoch() const { return time_since_epoch_; }
79
80     // Other member functions...
81
82    private:
83     duration time_since_epoch_;
84   };
85
86  }  // namespace std::chrono
87
88What ``rep`` type should be used?
89=================================
90The duration's ``rep``, or tick count type, can be a floating point or a signed
91integer. For most applications, this is a signed integer just as how one may
92represent the number of ticks for an RTOS API or the number of nanoseconds in
93POSIX.
94
95The ``rep`` should be able to represent the durations of time necessary for the
96application. When possible, use ``int64_t`` as the ``rep`` for a clock's
97duration in order to trivially avoid integer underflow and overflow risks by
98covering a range of at least ±292 years. This matches the STL's requirements
99for the duration helper types which are relevant for a clock's tick period:
100
101* ``std::chrono::nanoseconds  duration</*signed integer type of at least 64 bits*/, std::nano>``
102* ``std::chrono::microseconds duration</*signed integer type of at least 55 bits*/, std::micro>``
103* ``std::chrono::milliseconds duration</*signed integer type of at least 45 bits*/, std::milli>``
104* ``std::chrono::seconds  duration</*signed integer type of at least 35 bits*/>``
105
106With this guidance one can avoid common pitfalls like ``uint32_t`` millisecond
107tick rollover bugs when using RTOSes every 49.7 days.
108
109.. warning::
110   Avoid the ``duration<>::min()`` and ``duration<>::max()`` helper member
111   functions where possible as they exceed the ±292 years duration limit
112   assumption. There's an immediate risk of integer underflow or overflow for
113   any arithmetic operations. Consider using ``std::optional`` instead of
114   priming a variable with a value at the limit.
115
116Helper duration types and literals
117==================================
118The STL's ``<chrono>`` library includes a set of helper types based on actual
119time units, including the following (and more):
120
121* ``std::chrono::nanoseconds``
122* ``std::chrono::microseconds``
123* ``std::chrono::milliseconds``
124* ``std::chrono::seconds``
125* ``std::chrono::minutes``
126* ``std::chrono::hours``
127
128As an example you can use these as follows:
129
130.. code-block:: cpp
131
132   #include <chrono>
133
134   void Foo() {
135     Bar(std::chrono::milliseconds(42));
136   }
137
138In addition, the inline namespace ``std::literals::chrono_literals`` includes:
139
140* ``operator""ns`` for ``std::chrono::nanoseconds``
141* ``operator""us`` for ``std::chrono::microseconds``
142* ``operator""ms`` for ``std::chrono::milliseconds``
143* ``operator""s`` for ``std::chrono::seconds``
144* ``operator""min`` for ``std::chrono::minutes``
145* ``operator""h`` for ``std::chrono::hours``
146
147As an example you can use these as follows:
148
149.. code-block:: cpp
150
151   using std::literals::chrono_literals::ms;
152   // Or if you want them all: using namespace std::chrono_literals;
153
154   void Foo() {
155     Bar(42ms);
156   }
157
158For these helper duration types to be compatible with API's that take a
159`SystemClock::duration` either an :ref:`implicit<Implicit lossless conversions>`
160or :ref:`explicit lossy<Explicit lossy conversions>` conversion must be done.
161
162Converting between time units and clock durations
163=================================================
164So why go through all of this trouble instead of just using ticks or instead
165just using one time unit such as nanoseconds? For example, imagine that you have
166a 1kHz RTOS tick period and you would like to express a timeout duration:
167
168.. code-block:: cpp
169
170   // Instead of using ticks which are not portable between RTOS configurations,
171   // as the tick period may be different:
172   constexpr uint32_t kFooNotificationTimeoutTicks = 42;
173   bool TryGetNotificationFor(uint32_t ticks);
174
175   // And instead of using a time unit which is prone to accidental conversion
176   // errors as all variables must maintain the time units:
177   constexpr uint32_t kFooNotificationTimeoutMs = 42;
178   bool TryGetNotificationFor(uint32_t milliseconds);
179
180   // We can instead use a defined clock and its duration for the kernel and rely
181   // on implicit lossless conversions:
182   #include <chrono>
183   #include "pw_chrono/system_clock.h"
184   constexpr SystemClock::duration kFooNotificationTimeout =
185       std::chrono::milliseconds(42);
186   bool TryGetNotificationFor(SystemClock::duration timeout);
187
188   void MaybeProcessNotification() {
189     if (TryGetNotificationFor(kFooNotificationTimeout)) {
190       ProcessNotification();
191     }
192   }
193
194.. _Implicit lossless conversions:
195
196Implicit lossless conversions
197-----------------------------
198Wait, but how does this work? Is there a hidden cost? The ``duration`` type
199comes with built in implicit lossless conversion support which is evaluated at
200compile time where possible.
201
202If you rely on implicit conversions then the worst case cost is multiplication,
203there is no risk of a division operation.
204
205If the implicit conversion cannot be guaranteed at compile time to be lossless
206for all possible tick count values, then it will fail to compile.
207
208As an example you can always convert from ``std::chrono::seconds`` to
209``std::chrono::milliseconds`` in a lossless manner. However, you cannot
210guarantee for all tick count values that ``std::chrono::milliseconds`` can be
211losslessly converted to ``std::chrono::seconds``, even though it may work for
212some values like ``0``, ``1000``, etc.
213
214.. code-block:: cpp
215
216   #include <chrono>
217
218   constexpr std::chrono::milliseconds this_compiles =
219       std::chrono::seconds(42);
220
221   // This cannot compile, because for some duration values it is lossy even
222   // though this particular value can be in theory converted to whole seconds.
223   // constexpr std::chrono::seconds this_does_not_compile =
224   //    std::chrono::milliseconds(1000);
225
226.. _Explicit lossy conversions:
227
228Explicit lossy conversions
229--------------------------
230While code should prefer implicit lossless conversions whenever possible,
231sometimes a lossy conversion is required.
232
233Consider an example where a RTOS employs a 128Hz tick clock. The 128Hz
234``period`` can be perfectly represented with a ``std::ratio<1,128>``. However
235you will not be able to implicitly convert any real time unit durations to this
236duration type. Instead explicit lossy conversions must be used. Pigweed
237recommends explicitly using:
238
239* `std::chrono::floor <https://en.cppreference.com/w/cpp/chrono/duration/floor>`_
240  to round down.
241* `std::chrono::round <https://en.cppreference.com/w/cpp/chrono/duration/round>`_
242  to round to the nearest, rounding to even in halfway cases.
243* `std::chrono::ceil <https://en.cppreference.com/w/cpp/chrono/duration/ceil>`_
244  to round up.
245* `pw::chrono::SystemClock::for_at_least` to round up using the `SystemClock::period`,
246  as a more explicit form of std::chrono::ceil.
247
248.. Note::
249   Pigweed does not recommend using ``std::chrono::duration_cast<>`` which
250   truncates dowards zero like ``static_cast``. This is typically not the desired
251   rounding behavior when dealing with time units. Instead, where possible we
252   recommend the more explicit, self-documenting ``std::chrono::floor``,
253   ``std::chrono::round``, and ``std::chrono::ceil``.
254
255Now knowing this, the previous example could be portably and correctly handled
256as follows:
257
258.. code-block:: cpp
259
260   #include <chrono>
261
262   #include "pw_chrono/system_clock.h"
263
264   // We want to round up to ensure we block for at least the specified duration,
265   // instead of rounding down. Imagine for example the extreme case where you
266   // may round down to zero or one, you would definitely want to at least block.
267   constexpr SystemClock::duration kFooNotificationTimeout =
268       std::chrono::ceil(std::chrono::milliseconds(42));
269   bool TryGetNotificationFor(SystemClock::duration timeout);
270
271   void MaybeProcessNotification() {
272     if (TryGetNotificationFor(kFooNotificationTimeout)) {
273       ProcessNotification();
274     }
275   }
276
277This code is lossless if the clock period is 1kHz and it's correct using a
278division which rounds up when the clock period is 128Hz.
279
280.. Note::
281   When using ``pw::chrono::SystemClock::duration`` for timeouts, prefer
282   using its ``SystemClock::for_at_least()`` to round up timeouts in a more
283   explicit, self documenting manner which uses ``std::chrono::ceil``
284   internally.
285
286Use of ``count()`` and ``time_since_epoch()``
287=============================================
288It's easy to escape the typesafe chrono types through the use of
289``duration<>::count()`` and ``time_point<>::time_since_epoch()``, however this
290increases the risk of accidentally introduce conversion and arithmetic errors.
291
292For this reason, avoid these two escape hatches until it's absolutely necessary
293due to I/O such as RPCs or writing to non-volatile storage.
294
295Discrete Timeouts
296=================
297We briefly want to mention a common pitfall when working with discrete
298representations of time durations for timeouts (ticks and real time units)
299on systems with a continously running clock which is backed by discrete time
300intervals (i.e. whole integer constant tick periods).
301
302Imagine an RTOS system where we have a constant tick interval. If we attempt to
303sleep for 1 tick, how long will the kernel actually let us sleep?
304
305In most kernels you will end up sleeping somewhere between 0 and 1 tick periods
306inclusively, i.e. ``[0, 1]``, if we ignore scheduling latency and preemption.
307**This means it can randomly be non-blocking vs blocking!**
308
309This is because internally kernels use a decrementing timeout counter or a
310deadline without taking the current current progression through the existing
311tick period into account.
312
313For this reason all of Pigweed's time bound APIs will internally add an extra
314tick to timeout intents when needed to guarantee that we will block for at least
315the specified timeout.
316
317This same risk exists if a continuously running hardware timer is used for a
318software timer service.
319
320.. Note::
321   When calculating deadlines based on a ``pw::chrono::SystemClock::timeout``,
322   use ``SystemClock::TimePointAfterAtLeast()`` which adds an extra tick for you
323   internally.
324
325------
326Clocks
327------
328We do not recomend using the clocks provided by ``<chrono>`` including but not
329limited to the ``std::chrono::system_clock``, ``std::chrono::steady_clock``, and
330``std::chrono::high_resolution_clock``. These clocks typically do not work on
331embedded systems, as they are not backed by any actual clocks although they
332often do compile. In addition, their APIs miss guarantees and parameters which
333make them difficult and risky to use on embedded systems.
334
335In addition, the STL time bound APIs heavily rely on templating to permit
336different clocks and durations to be used. We believe this level of template
337metaprogramming and the indirection that comes with that can be confusing. On
338top of this, accidental use of the wrong clock and/or conversions between them
339is a frequent source of bugs. For example using a real time clock which is not
340monotonic for a timeout or deadline can wreak havoc when the clock is adjusted.
341
342For this reason Pigweed's timeout and deadline APIs will not permit arbitrary
343clock and duration selection. Outside of small templated helpers, all APIs will
344require a specific clock's duration and/or time-point. For almost all of Pigweed
345this means that the ``pw::chrono::SystemClock`` is used which is usually backed
346by the kernel's clock.
347
348PigweedClock Requirements
349=========================
350``pw_chrono`` extends the C++ named
351`Clock <https://en.cppreference.com/w/cpp/named_req/Clock>`_ and
352`TrivialClock <https://en.cppreference.com/w/cpp/named_req/TrivialClock>`_
353requirements with the ``PigweedClock Requirements`` to make clocks more friendly
354for embedded systems.
355
356This permits the clock compatibility to be verified through ``static_assert`` at
357compile time which the STL's requirements do not address. For example whether
358the clock continues to tick while interrupts are masked or whether the clock is
359monotonic even if the clock period may not be steady due to the use of low power
360sleep modes.
361
362For a type ``PWC`` to meet the ``PigweedClock Requirements``:
363
364* The type PWC must meet C++14's
365  `Clock <https://en.cppreference.com/w/cpp/named_req/Clock>`_ and
366  `TrivialClock <https://en.cppreference.com/w/cpp/named_req/TrivialClock>`_
367  requirements.
368* The ``PWC::rep`` must be ``int64_t`` to ensure that there cannot be any
369  overflow risk regardless of the ``PWC::period`` configuration.
370  This is done because we do not expect any clocks with periods coarser than
371  seconds which already require 35 bits.
372* ``const bool PWC::is_monotonic`` must return true if and only if the clock
373  can never move backwards.
374  This effectively allows one to describe an unsteady but monotonic clock by
375  combining the C++14's Clock requirement's ``const bool PWC::is_steady``.
376* ``const bool PWC::is_free_running`` must return true if and only if the clock
377  continues to move forward, without risk of overflow, regardless of whether
378  global interrupts are disabled or whether one is in a critical section or even
379  non maskable interrupt.
380* ``const bool PWC::is_always_enabled`` must return true if the clock is always
381  enabled and available. If false, the clock must:
382
383  + Ensure the ``const bool is_{steady,monotonic,free_running}`` attributes
384    are all valid while the clock is not enabled to ensure they properly meet
385    the previously stated requirements.
386  + Meet C++14's
387    `BasicLockable <https://en.cppreference.com/w/cpp/named_req/BasicLockable>`_
388    requirements (i.e. provide ``void lock()`` & ``void unlock()``) in order
389    to provide ``std::scoped_lock`` support to enable a user to enable the
390    clock.
391  + Provide ``const bool is_{steady,monotonic,free_running}_while_enabled``
392    attributes which meet the attributes only while the clock is enabled.
393* ``const bool PWC::is_stopped_in_halting_debug_mode`` must return true if and
394  only if the clock halts, without further modification, during halting debug
395  mode , for example during a breakpoint while a hardware debugger is used.
396* ``const Epoch PWC::epoch`` must return the epoch type of the clock, the
397  ``Epoch`` enumeration is defined in ``pw_chrono/epoch.h``.
398* The function ``time_point PWC::now() noexcept`` must always be thread and
399  interrupt safe, but not necessarily non-masking and bare-metal interrupt safe.
400* ``const bool PWC::is_non_masking_interrupt_safe`` must return true if and only
401  if the clock is safe to use from non-masking and bare-metal interrupts.
402
403The PigweedClock requirement will not require ``now()`` to be a static function,
404however the upstream façades will follow this approach.
405
406SystemClock facade
407==================
408The ``pw::chrono::SystemClock`` is meant to serve as the clock used for time
409bound operations such as thread sleeping, waiting on mutexes/semaphores, etc.
410The ``SystemClock`` always uses a signed 64 bit as the underlying type for time
411points and durations. This means users do not have to worry about clock overflow
412risk as long as rational durations and time points as used, i.e. within a range
413of ±292 years.
414
415The ``SystemClock`` represents an unsteady, monotonic clock.
416
417The epoch of this clock is unspecified and may not be related to wall time
418(for example, it can be time since boot). The time between ticks of this
419clock may vary due to sleep modes and potential interrupt handling.
420``SystemClock`` meets the requirements of C++'s ``TrivialClock`` and Pigweed's
421``PigweedClock``.
422
423This clock is used for expressing timeout and deadline semantics with the
424scheduler in Pigweed including pw_sync, pw_thread, etc.
425
426C++
427---
428.. doxygenstruct:: pw::chrono::SystemClock
429   :members:
430
431Example in C++
432--------------
433.. code-block:: cpp
434
435   #include <chrono>
436
437   #include "pw_chrono/system_clock.h"
438
439   void Foo() {
440     const SystemClock::time_point before = SystemClock::now();
441     TakesALongTime();
442     const SystemClock::duration time_taken = SystemClock::now() - before;
443     bool took_way_too_long = false;
444     if (time_taken > std::chrono::seconds(42)) {
445       took_way_too_long = true;
446     }
447   }
448
449VirtualClock
450============
451Pigweed also includes a virtual base class for timers,
452:cpp:class:`pw::chrono::VirtualClock`. This class allows for writing
453timing-sensitive code that can be tested using simulated clocks such as
454:cpp:class:`pw::chrono::SimulatedSystemClock`.
455
456Using simulated clocks in tests allow tests to avoid sleeping or timeouts,
457resulting in faster and more reliable tests.
458
459See also :cpp:class:`pw::async2::TimeProvider` for creating testable
460time-sensitive code using asynchronous timers.
461
462.. doxygenclass:: pw::chrono::VirtualClock
463  :members:
464
465.. doxygenclass:: pw::chrono::SimulatedSystemClock
466  :members:
467
468
469
470Protobuf
471========
472Sometimes it's desirable to communicate high resolution time points and
473durations from one device to another. For this, ``pw_chrono`` provides protobuf
474representations of clock parameters (``pw.chrono.ClockParameters``) and time
475points (``pw.chrono.TimePoint``). These types are less succinct than simple
476single-purpose fields like ``ms_since_boot`` or ``unix_timestamp``, but allow
477timestamps to be communicated in terms of the tick rate of a device, potentially
478providing significantly higher resolution. Logging, tracing, and system state
479snapshots are use cases that benefit from this additional resolution.
480
481This module provides an overlay proto (``pw.chrono.SnapshotTimestamps``) for
482usage with ``pw_snapshot`` to encourage capture of high resolution timestamps
483in device snapshots. Simplified capture utilies and host-side tooling to
484interpret this data are not yet provided by ``pw_chrono``.
485
486There is tooling that take these proto and make them more human readable.
487
488---------------
489Software Timers
490---------------
491
492SystemTimer facade
493==================
494The SystemTimer facade enables deferring execution of a callback until a later
495time. For example, enabling low power mode after a period of inactivity.
496
497The base SystemTimer only supports a one-shot style timer with a callback.
498A periodic timer can be implemented by rescheduling the timer in the callback
499through ``InvokeAt(kDesiredPeriod + expired_deadline)``.
500
501When implementing a periodic layer on top, the user should be mindful of
502handling missed periodic callbacks. They could opt to invoke the callback
503multiple times with the expected ``expired_deadline`` values or instead saturate
504and invoke the callback only once with the latest ``expired_deadline``.
505
506The entire API is thread safe, however it is NOT always IRQ safe.
507
508The ExpiryCallback is either invoked from a high priority thread or an
509interrupt. Ergo ExpiryCallbacks should be treated as if they are executed by an
510interrupt, meaning:
511
512* Processing inside of the callback should be kept to a minimum.
513
514* Callbacks should never attempt to block.
515
516* APIs which are not interrupt safe such as pw::sync::Mutex should not be used!
517
518C++
519---
520.. doxygenclass:: pw::chrono::SystemTimer
521   :members:
522
523.. cpp:namespace-push:: pw::chrono::SystemTimer
524
525.. list-table::
526   :widths: 70 10 10 10
527   :header-rows: 1
528
529   * - Safe to use in context
530     - Thread
531     - Interrupt
532     - NMI
533   * - :cpp:func:`pw::chrono::SystemTimer::SystemTimer`
534     - ✔
535     -
536     -
537   * - :cpp:func:`pw::chrono::SystemTimer::~SystemTimer`
538     - ✔
539     -
540     -
541   * - :cpp:func:`InvokeAfter`
542     - ✔
543     -
544     -
545   * - :cpp:func:`InvokeAt`
546     - ✔
547     -
548     -
549   * - :cpp:func:`Cancel`
550     - ✔
551     -
552     -
553
554.. cpp:namespace-pop::
555
556Example in C++
557--------------
558.. code-block:: cpp
559
560   #include "pw_chrono/system_clock.h"
561   #include "pw_chrono/system_timer.h"
562   #include "pw_log/log.h"
563
564   using namespace std::chrono_literals;
565
566   void DoFoo(pw::chrono::SystemClock::time_point expired_deadline) {
567     PW_LOG_INFO("Timer callback invoked!");
568   }
569
570   pw::chrono::SystemTimer foo_timer(DoFoo);
571
572   void DoFooLater() {
573     foo_timer.InvokeAfter(42ms);  // DoFoo will be invoked after 42ms.
574   }
575
576.. _module-pw_chrono-libc-time-wrappers:
577
578------------------
579libc time wrappers
580------------------
581The `gettimeofday <https://pubs.opengroup.org/onlinepubs/9699919799/functions/gettimeofday.html>`__
582and `time <https://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html>`__
583POSIX functions are defined to return the current time since the Epoch.
584The default ``pw_toolchain/arg_gcc:newlib_os_interface_stubs`` stub for
585``gettimeofday`` will cause a linker error if any code tried to use this
586function, but it's common for software not written for embedded systems to
587depend on this function being defined and returning something that increments
588like a clock. In addition, some software depends on having ``gettimeofday``
589return something much closer to the actual time so it can compare against well
590known time points inside TLS certificates for instance.
591
592For compatibility with such software, ``pw_toolchain`` provides two different
593options to wrap libc time functions. Both of these are not recommended for
594general time querying and are only intended to provide compatibility.
595
596``pw_chrono:wrap_time_build_time``
597==================================
598Wrap ``gettimeofday`` and ``time`` with an implementation that returns a static
599time value at which the library was built. Use this option if you need these
600functions to return a known value greater than some point in the past.
601
602.. note::
603   When building with Bazel, use the `--stamp
604   <https://bazel.build/docs/user-manual#stamp>`__ flag when building release
605   binaries to ensure the build time reflects the actual time the build is
606   executed, as opposed to a cached value.
607
608``pw_chrono:wrap_time_system_clock``
609====================================
610Wrap ``gettimeofday`` and ``time`` with an implementation that uses
611``pw::chrono::SystemClock`` to return the current time. Note the epoch is
612determined by the SystemClock backend epoch, which on most embedded systems will
613be time since boot. Use this option if you don't care about the time returned
614being close to actual time, but do care that it increments like a real clock.
615
616.. toctree::
617   :hidden:
618   :maxdepth: 1
619
620   Backends <backends>
621