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