LCOV - code coverage report
Current view: top level - zephyr/sys - timeutil.h Coverage Total Hit
Test: new.info Lines: 72.5 % 40 29
Test Date: 2025-09-05 16:43:28

            Line data    Source code
       1            1 : /*
       2              :  * Copyright (c) 2019 Peter Bigot Consulting, LLC
       3              :  * Copyright (c) 2025 Tenstorrent AI ULC
       4              :  *
       5              :  * SPDX-License-Identifier: Apache-2.0
       6              :  */
       7              : 
       8              : /**
       9              :  * @file
      10              :  * @brief Utilities supporting operation on time data structures.
      11              :  *
      12              :  * POSIX defines gmtime() to convert from time_t to struct tm, but all
      13              :  * inverse transformations are non-standard or require access to time
      14              :  * zone information.  timeutil_timegm() implements the functionality
      15              :  * of the GNU extension timegm() function, but changes the error value
      16              :  * as @c EOVERFLOW is not a standard C error identifier.
      17              :  *
      18              :  * timeutil_timegm64() is provided to support full precision
      19              :  * conversion on platforms where @c time_t is limited to 32 bits.
      20              :  */
      21              : 
      22              : #ifndef ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_
      23              : #define ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_
      24              : 
      25              : #include <limits.h>
      26              : #include <stdbool.h>
      27              : #include <stddef.h>
      28              : #include <stdint.h>
      29              : #include <time.h>
      30              : 
      31              : #include <zephyr/sys_clock.h>
      32              : #include <zephyr/sys/__assert.h>
      33              : #include <zephyr/sys/math_extras.h>
      34              : #include <zephyr/sys/time_units.h>
      35              : #include <zephyr/sys/util.h>
      36              : #include <zephyr/toolchain.h>
      37              : #include <zephyr/types.h>
      38              : 
      39              : #ifdef __cplusplus
      40              : extern "C" {
      41              : #endif
      42              : 
      43              : /* Maximum and minimum value TIME_T can hold */
      44            0 : #define SYS_TIME_T_MAX ((((time_t)1 << (8 * sizeof(time_t) - 2)) - 1) * 2 + 1)
      45            0 : #define SYS_TIME_T_MIN (-SYS_TIME_T_MAX - 1)
      46              : 
      47              : /* Converts ticks to seconds, discarding any fractional seconds */
      48            0 : #define SYS_TICKS_TO_SECS(ticks)                                                                   \
      49              :         (((uint64_t)(ticks) >= (uint64_t)K_TICKS_FOREVER) ? SYS_TIME_T_MAX                         \
      50              :                                                           : k_ticks_to_sec_floor64(ticks))
      51              : 
      52              : /* Converts ticks to nanoseconds, modulo NSEC_PER_SEC */
      53            0 : #define SYS_TICKS_TO_NSECS(ticks)                                                                  \
      54              :         (((uint64_t)(ticks) >= (uint64_t)K_TICKS_FOREVER)                                          \
      55              :                  ? (NSEC_PER_SEC - 1)                                                              \
      56              :                  : k_ticks_to_ns_floor32((uint64_t)(ticks) % CONFIG_SYS_CLOCK_TICKS_PER_SEC))
      57              : 
      58              : /* Define a timespec */
      59            0 : #define SYS_TIMESPEC(sec, nsec)                                                                    \
      60              :         ((struct timespec){                                                                        \
      61              :                 .tv_sec = (time_t)CLAMP((int64_t)(sec), SYS_TIME_T_MIN, SYS_TIME_T_MAX),           \
      62              :                 .tv_nsec = (long)(nsec),                                                           \
      63              :         })
      64              : 
      65              : /* Initialize a struct timespec object from a tick count */
      66            0 : #define SYS_TICKS_TO_TIMESPEC(ticks) SYS_TIMESPEC(SYS_TICKS_TO_SECS(ticks), \
      67              :                                                   SYS_TICKS_TO_NSECS(ticks))
      68              : 
      69              : /* The semantic equivalent of K_NO_WAIT but expressed as a timespec object*/
      70            0 : #define SYS_TIMESPEC_NO_WAIT SYS_TICKS_TO_TIMESPEC(0)
      71              : 
      72              : /* The semantic equivalent of K_TICK_MIN but expressed as a timespec object */
      73            0 : #define SYS_TIMESPEC_MIN SYS_TICKS_TO_TIMESPEC(K_TICK_MIN)
      74              : 
      75              : /* The semantic equivalent of K_TICK_MAX but expressed as a timespec object */
      76            0 : #define SYS_TIMESPEC_MAX SYS_TICKS_TO_TIMESPEC(K_TICK_MAX)
      77              : 
      78              : /* The semantic equivalent of K_FOREVER but expressed as a timespec object*/
      79            0 : #define SYS_TIMESPEC_FOREVER SYS_TIMESPEC(SYS_TIME_T_MAX, NSEC_PER_SEC - 1)
      80              : 
      81              : /**
      82              :  * @defgroup timeutil_apis Time Utility APIs
      83              :  * @ingroup utilities
      84              :  * @defgroup timeutil_repr_apis Time Representation APIs
      85              :  * @ingroup timeutil_apis
      86              :  * @{
      87              :  */
      88              : 
      89              : /* Base Year value use in calculations in "timeutil_timegm64" API */
      90            0 : #define TIME_UTILS_BASE_YEAR 1900
      91              : 
      92              : /**
      93              :  * @brief Convert broken-down time to a POSIX epoch offset in seconds.
      94              :  *
      95              :  * @param tm pointer to broken down time.
      96              :  *
      97              :  * @return the corresponding time in the POSIX epoch time scale.
      98              :  *
      99              :  * @see http://man7.org/linux/man-pages/man3/timegm.3.html
     100              :  */
     101            1 : int64_t timeutil_timegm64(const struct tm *tm);
     102              : 
     103              : /**
     104              :  * @brief Convert broken-down time to a POSIX epoch offset in seconds.
     105              :  *
     106              :  * @param tm pointer to broken down time.
     107              :  *
     108              :  * @return the corresponding time in the POSIX epoch time scale.  If
     109              :  * the time cannot be represented then @c (time_t)-1 is returned and
     110              :  * @c errno is set to @c ERANGE`.
     111              :  *
     112              :  * @see http://man7.org/linux/man-pages/man3/timegm.3.html
     113              :  */
     114            1 : time_t timeutil_timegm(const struct tm *tm);
     115              : 
     116              : /**
     117              :  * @}
     118              :  * @defgroup timeutil_sync_apis Time Synchronization APIs
     119              :  * @ingroup timeutil_apis
     120              :  * @{
     121              :  */
     122              : 
     123              : /**
     124              :  * @brief Immutable state for synchronizing two clocks.
     125              :  *
     126              :  * Values required to convert durations between two time scales.
     127              :  *
     128              :  * @note The accuracy of the translation and calculated skew between sources
     129              :  * depends on the resolution of these frequencies.  A reference frequency with
     130              :  * microsecond or nanosecond resolution would produce the most accurate
     131              :  * tracking when the local reference is the Zephyr tick counter.  A reference
     132              :  * source like an RTC chip with 1 Hz resolution requires a much larger
     133              :  * interval between sampled instants to detect relative clock drift.
     134              :  */
     135            1 : struct timeutil_sync_config {
     136              :         /** The nominal instance counter rate in Hz.
     137              :          *
     138              :          * This value is assumed to be precise, but may drift depending on
     139              :          * the reference clock source.
     140              :          *
     141              :          * The value must be positive.
     142              :          */
     143            1 :         uint32_t ref_Hz;
     144              : 
     145              :         /** The nominal local counter rate in Hz.
     146              :          *
     147              :          * This value is assumed to be inaccurate but reasonably stable.  For
     148              :          * a local clock driven by a crystal oscillator an error of 25 ppm is
     149              :          * common; for an RC oscillator larger errors should be expected.  The
     150              :          * timeutil_sync infrastructure can calculate the skew between the
     151              :          * local and reference clocks and apply it when converting between
     152              :          * time scales.
     153              :          *
     154              :          * The value must be positive.
     155              :          */
     156            1 :         uint32_t local_Hz;
     157              : };
     158              : 
     159              : /**
     160              :  * @brief Representation of an instant in two time scales.
     161              :  *
     162              :  * Capturing the same instant in two time scales provides a
     163              :  * registration point that can be used to convert between those time
     164              :  * scales.
     165              :  */
     166            1 : struct timeutil_sync_instant {
     167              :         /** An instant in the reference time scale.
     168              :          *
     169              :          * This must never be zero in an initialized timeutil_sync_instant
     170              :          * object.
     171              :          */
     172            1 :         uint64_t ref;
     173              : 
     174              :         /** The corresponding instance in the local time scale.
     175              :          *
     176              :          * This may be zero in a valid timeutil_sync_instant object.
     177              :          */
     178            1 :         uint64_t local;
     179              : };
     180              : 
     181              : /**
     182              :  * @brief State required to convert instants between time scales.
     183              :  *
     184              :  * This state in conjunction with functions that manipulate it capture
     185              :  * the offset information necessary to convert between two timescales
     186              :  * along with information that corrects for skew due to inaccuracies
     187              :  * in clock rates.
     188              :  *
     189              :  * State objects should be zero-initialized before use.
     190              :  */
     191            1 : struct timeutil_sync_state {
     192              :         /** Pointer to reference and local rate information. */
     193            1 :         const struct timeutil_sync_config *cfg;
     194              : 
     195              :         /** The base instant in both time scales. */
     196            1 :         struct timeutil_sync_instant base;
     197              : 
     198              :         /** The most recent instant in both time scales.
     199              :          *
     200              :          * This is captured here to provide data for skew calculation.
     201              :          */
     202            1 :         struct timeutil_sync_instant latest;
     203              : 
     204              :         /** The scale factor used to correct for clock skew.
     205              :          *
     206              :          * The nominal rate for the local counter is assumed to be
     207              :          * inaccurate but stable, i.e. it will generally be some
     208              :          * parts-per-million faster or slower than specified.
     209              :          *
     210              :          * A duration in observed local clock ticks must be multiplied by
     211              :          * this value to produce a duration in ticks of a clock operating at
     212              :          * the nominal local rate.
     213              :          *
     214              :          * A zero value indicates that the skew has not been initialized.
     215              :          * If the value is zero when #base is initialized the skew will be
     216              :          * set to 1.  Otherwise the skew is assigned through
     217              :          * timeutil_sync_state_set_skew().
     218              :          */
     219            1 :         float skew;
     220              : };
     221              : 
     222              : /**
     223              :  * @brief Record a new instant in the time synchronization state.
     224              :  *
     225              :  * Note that this updates only the latest persisted instant.  The skew
     226              :  * is not adjusted automatically.
     227              :  *
     228              :  * @param tsp pointer to a timeutil_sync_state object.
     229              :  *
     230              :  * @param inst the new instant to be recorded.  This becomes the base
     231              :  * instant if there is no base instant, otherwise the value must be
     232              :  * strictly after the base instant in both the reference and local
     233              :  * time scales.
     234              :  *
     235              :  * @retval 0 if installation succeeded in providing a new base
     236              :  * @retval 1 if installation provided a new latest instant
     237              :  * @retval -EINVAL if the new instant is not compatible with the base instant
     238              :  */
     239            1 : int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
     240              :                                const struct timeutil_sync_instant *inst);
     241              : 
     242              : /**
     243              :  * @brief Update the state with a new skew and possibly base value.
     244              :  *
     245              :  * Set the skew from a value retrieved from persistent storage, or
     246              :  * calculated based on recent skew estimations including from
     247              :  * timeutil_sync_estimate_skew().
     248              :  *
     249              :  * Optionally update the base timestamp.  If the base is replaced the
     250              :  * latest instant will be cleared until timeutil_sync_state_update() is
     251              :  * invoked.
     252              :  *
     253              :  * @param tsp pointer to a time synchronization state.
     254              :  *
     255              :  * @param skew the skew to be used.  The value must be positive and
     256              :  * shouldn't be too far away from 1.
     257              :  *
     258              :  * @param base optional new base to be set.  If provided this becomes
     259              :  * the base timestamp that will be used along with skew to convert
     260              :  * between reference and local timescale instants.  Setting the base
     261              :  * clears the captured latest value.
     262              :  *
     263              :  * @return 0 if skew was updated
     264              :  * @return -EINVAL if skew was not valid
     265              :  */
     266            1 : int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
     267              :                                  const struct timeutil_sync_instant *base);
     268              : 
     269              : /**
     270              :  * @brief Estimate the skew based on current state.
     271              :  *
     272              :  * Using the base and latest syncpoints from the state determine the
     273              :  * skew of the local clock relative to the reference clock.  See
     274              :  * timeutil_sync_state::skew.
     275              :  *
     276              :  * @param tsp pointer to a time synchronization state.  The base and latest
     277              :  * syncpoints must be present and the latest syncpoint must be after
     278              :  * the base point in the local time scale.
     279              :  *
     280              :  * @return the estimated skew, or zero if skew could not be estimated.
     281              :  */
     282            1 : float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp);
     283              : 
     284              : /**
     285              :  * @brief Interpolate a reference timescale instant from a local
     286              :  * instant.
     287              :  *
     288              :  * @param tsp pointer to a time synchronization state.  This must have a base
     289              :  * and a skew installed.
     290              :  *
     291              :  * @param local an instant measured in the local timescale.  This may
     292              :  * be before or after the base instant.
     293              :  *
     294              :  * @param refp where the corresponding instant in the reference
     295              :  * timescale should be stored.  A negative interpolated reference time
     296              :  * produces an error.  If interpolation fails the referenced object is
     297              :  * not modified.
     298              :  *
     299              :  * @retval 0 if interpolated using a skew of 1
     300              :  * @retval 1 if interpolated using a skew not equal to 1
     301              :  * @retval -EINVAL
     302              :  *   * the times synchronization state is not adequately initialized
     303              :  *   * @p refp is null
     304              :  * @retval -ERANGE the interpolated reference time would be negative
     305              :  */
     306            1 : int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
     307              :                                  uint64_t local, uint64_t *refp);
     308              : 
     309              : /**
     310              :  * @brief Interpolate a local timescale instant from a reference
     311              :  * instant.
     312              :  *
     313              :  * @param tsp pointer to a time synchronization state.  This must have a base
     314              :  * and a skew installed.
     315              :  *
     316              :  * @param ref an instant measured in the reference timescale.  This
     317              :  * may be before or after the base instant.
     318              :  *
     319              :  * @param localp where the corresponding instant in the local
     320              :  * timescale should be stored.  An interpolated value before local
     321              :  * time 0 is provided without error.  If interpolation fails the
     322              :  * referenced object is not modified.
     323              :  *
     324              :  * @retval 0 if successful with a skew of 1
     325              :  * @retval 1 if successful with a skew not equal to 1
     326              :  * @retval -EINVAL
     327              :  *   * the time synchronization state is not adequately initialized
     328              :  *   * @p refp is null
     329              :  */
     330            1 : int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
     331              :                                  uint64_t ref, int64_t *localp);
     332              : 
     333              : /**
     334              :  * @brief Convert from a skew to an error in parts-per-billion.
     335              :  *
     336              :  * A skew of 1.0 has zero error.  A skew less than 1 has a positive
     337              :  * error (clock is faster than it should be).  A skew greater than one
     338              :  * has a negative error (clock is slower than it should be).
     339              :  *
     340              :  * Note that due to the limited precision of @c float compared with @c
     341              :  * double the smallest error that can be represented is about 120 ppb.
     342              :  * A "precise" time source may have error on the order of 2000 ppb.
     343              :  *
     344              :  * A skew greater than 3.14748 may underflow the 32-bit
     345              :  * representation; this represents a clock running at less than 1/3
     346              :  * its nominal rate.
     347              :  *
     348              :  * @return skew error represented as parts-per-billion, or INT32_MIN
     349              :  * if the skew cannot be represented in the return type.
     350              :  */
     351            1 : int32_t timeutil_sync_skew_to_ppb(float skew);
     352              : 
     353              : /**
     354              :  * @}
     355              :  */
     356              : 
     357              : /**
     358              :  * @defgroup timeutil_timespec_apis Timespec Utility APIs
     359              :  * @ingroup timeutil_apis
     360              :  * @{
     361              :  */
     362              : 
     363              : /**
     364              :  * @brief Check if a timespec is valid.
     365              :  *
     366              :  * Check if a timespec is valid (i.e. normalized) by ensuring that the `tv_nsec` field is in the
     367              :  * range `[0, NSEC_PER_SEC-1]`.
     368              :  *
     369              :  * @note @p ts must not be `NULL`.
     370              :  *
     371              :  * @param ts the timespec to check
     372              :  *
     373              :  * @return `true` if the timespec is valid, otherwise `false`.
     374              :  */
     375            1 : static inline bool timespec_is_valid(const struct timespec *ts)
     376              : {
     377              :         __ASSERT_NO_MSG(ts != NULL);
     378              : 
     379              :         return (ts->tv_nsec >= 0) && (ts->tv_nsec < (long)NSEC_PER_SEC);
     380              : }
     381              : 
     382              : /**
     383              :  * @brief Normalize a timespec so that the `tv_nsec` field is in valid range.
     384              :  *
     385              :  * Normalize a timespec by adjusting the `tv_sec` and `tv_nsec` fields so that the `tv_nsec` field
     386              :  * is in the range `[0, NSEC_PER_SEC-1]`. This is achieved by converting nanoseconds to seconds and
     387              :  * accumulating seconds in either the positive direction when `tv_nsec` > `NSEC_PER_SEC`, or in the
     388              :  * negative direction when `tv_nsec` < 0.
     389              :  *
     390              :  * In pseudocode, normalization can be done as follows:
     391              :  * ```python
     392              :  * if ts.tv_nsec >= NSEC_PER_SEC:
     393              :  *     sec = ts.tv_nsec / NSEC_PER_SEC
     394              :  *     ts.tv_sec += sec
     395              :  *     ts.tv_nsec -= sec * NSEC_PER_SEC
     396              :  * elif ts.tv_nsec < 0:
     397              :  *      # div_round_up(abs(ts->tv_nsec), NSEC_PER_SEC)
     398              :  *          sec = (NSEC_PER_SEC - ts.tv_nsec - 1) / NSEC_PER_SEC
     399              :  *          ts.tv_sec -= sec;
     400              :  *          ts.tv_nsec += sec * NSEC_PER_SEC;
     401              :  * ```
     402              :  *
     403              :  * @note There are two cases where the normalization can result in integer overflow. These can
     404              :  * be extrapolated to not simply overflowing the `tv_sec` field by one second, but also by any
     405              :  * realizable multiple of `NSEC_PER_SEC`.
     406              :  *
     407              :  * 1. When `tv_nsec` is negative and `tv_sec` is already most negative.
     408              :  * 2. When `tv_nsec` is greater-or-equal to `NSEC_PER_SEC` and `tv_sec` is already most positive.
     409              :  *
     410              :  * If the operation would result in integer overflow, return value is `false`.
     411              :  *
     412              :  * @note @p ts must be non-`NULL`.
     413              :  *
     414              :  * @param ts the timespec to be normalized
     415              :  *
     416              :  * @return `true` if the operation completes successfully, otherwise `false`.
     417              :  */
     418            1 : static inline bool timespec_normalize(struct timespec *ts)
     419              : {
     420              :         __ASSERT_NO_MSG(ts != NULL);
     421              : 
     422              : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_add_overflow)
     423              : 
     424              :         int64_t sec = 0;
     425              :         int sign = (ts->tv_nsec >= 0) - (ts->tv_nsec < 0);
     426              : 
     427              :         /* only one of the following should be non-zero */
     428              :         sec += (ts->tv_nsec >= (long)NSEC_PER_SEC) * (ts->tv_nsec / (long)NSEC_PER_SEC);
     429              :         sec += ((sizeof(ts->tv_nsec) != sizeof(int64_t)) && (ts->tv_nsec != LONG_MIN) &&
     430              :                 (ts->tv_nsec < 0)) *
     431              :                DIV_ROUND_UP((unsigned long)-ts->tv_nsec, (long)NSEC_PER_SEC);
     432              :         sec += ((sizeof(ts->tv_nsec) == sizeof(int64_t)) && (ts->tv_nsec != INT64_MIN) &&
     433              :                 (ts->tv_nsec < 0)) *
     434              :                DIV_ROUND_UP((uint64_t)-ts->tv_nsec, NSEC_PER_SEC);
     435              :         sec += ((sizeof(ts->tv_nsec) != sizeof(int64_t)) && (ts->tv_nsec == LONG_MIN)) *
     436              :                ((LONG_MAX / NSEC_PER_SEC) + 1);
     437              :         sec += ((sizeof(ts->tv_nsec) == sizeof(int64_t)) && (ts->tv_nsec == INT64_MIN)) *
     438              :                ((INT64_MAX / NSEC_PER_SEC) + 1);
     439              : 
     440              :         ts->tv_nsec -= sec * sign * NSEC_PER_SEC;
     441              : 
     442              :         bool overflow = __builtin_add_overflow(ts->tv_sec, sign * sec, &ts->tv_sec);
     443              : 
     444              :         if (!overflow) {
     445              :                 __ASSERT_NO_MSG(timespec_is_valid(ts));
     446              :         }
     447              : 
     448              :         return !overflow;
     449              : 
     450              : #else
     451              : 
     452              :         long sec;
     453              : 
     454              :         if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
     455              :                 sec = ts->tv_nsec / (long)NSEC_PER_SEC;
     456              :         } else if (ts->tv_nsec < 0) {
     457              :                 sec = DIV_ROUND_UP((unsigned long)-ts->tv_nsec, NSEC_PER_SEC);
     458              :         } else {
     459              :                 sec = 0;
     460              :         }
     461              : 
     462              :         if ((ts->tv_nsec < 0) && (ts->tv_sec < 0) && (ts->tv_sec - SYS_TIME_T_MIN < sec)) {
     463              :                 /*
     464              :                  * When `tv_nsec` is negative and `tv_sec` is already most negative,
     465              :                  * further subtraction would cause integer overflow.
     466              :                  */
     467              :                 return false;
     468              :         }
     469              : 
     470              :         if ((ts->tv_nsec >= (long)NSEC_PER_SEC) && (ts->tv_sec > 0) &&
     471              :             (SYS_TIME_T_MAX - ts->tv_sec < sec)) {
     472              :                 /*
     473              :                  * When `tv_nsec` is >= `NSEC_PER_SEC` and `tv_sec` is already most
     474              :                  * positive, further addition would cause integer overflow.
     475              :                  */
     476              :                 return false;
     477              :         }
     478              : 
     479              :         if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
     480              :                 ts->tv_sec += sec;
     481              :                 ts->tv_nsec -= sec * (long)NSEC_PER_SEC;
     482              :         } else if (ts->tv_nsec < 0) {
     483              :                 ts->tv_sec -= sec;
     484              :                 ts->tv_nsec += sec * (long)NSEC_PER_SEC;
     485              :         } else {
     486              :                 /* no change: SonarQube was complaining */
     487              :         }
     488              : 
     489              :         __ASSERT_NO_MSG(timespec_is_valid(ts));
     490              : 
     491              :         return true;
     492              : #endif
     493              : }
     494              : 
     495              : /**
     496              :  * @brief Add one timespec to another
     497              :  *
     498              :  * This function sums the two timespecs pointed to by @p a and @p b and stores the result in the
     499              :  * timespce pointed to by @p a.
     500              :  *
     501              :  * If the operation would result in integer overflow, return value is `false`.
     502              :  *
     503              :  * @note @p a and @p b must be non-`NULL` and normalized.
     504              :  *
     505              :  * @param a the timespec which is added to
     506              :  * @param b the timespec to be added
     507              :  *
     508              :  * @return `true` if the operation was successful, otherwise `false`.
     509              :  */
     510            1 : static inline bool timespec_add(struct timespec *a, const struct timespec *b)
     511              : {
     512              :         __ASSERT_NO_MSG((a != NULL) && timespec_is_valid(a));
     513              :         __ASSERT_NO_MSG((b != NULL) && timespec_is_valid(b));
     514              : 
     515              : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_add_overflow)
     516              : 
     517              :         return !__builtin_add_overflow(a->tv_sec, b->tv_sec, &a->tv_sec) &&
     518              :                !__builtin_add_overflow(a->tv_nsec, b->tv_nsec, &a->tv_nsec) &&
     519              :                timespec_normalize(a);
     520              : 
     521              : #else
     522              : 
     523              :         if ((a->tv_sec < 0) && (b->tv_sec < 0) && (SYS_TIME_T_MIN - a->tv_sec > b->tv_sec)) {
     524              :                 /* negative integer overflow would occur */
     525              :                 return false;
     526              :         }
     527              : 
     528              :         if ((a->tv_sec > 0) && (b->tv_sec > 0) && (SYS_TIME_T_MAX - a->tv_sec < b->tv_sec)) {
     529              :                 /* positive integer overflow would occur */
     530              :                 return false;
     531              :         }
     532              : 
     533              :         a->tv_sec += b->tv_sec;
     534              :         a->tv_nsec += b->tv_nsec;
     535              : 
     536              :         return timespec_normalize(a);
     537              : 
     538              : #endif
     539              : }
     540              : 
     541              : /**
     542              :  * @brief Negate a timespec object
     543              :  *
     544              :  * Negate the timespec object pointed to by @p ts and store the result in the same
     545              :  * memory location.
     546              :  *
     547              :  * If the operation would result in integer overflow, return value is `false`.
     548              :  *
     549              :  * @param ts The timespec object to negate.
     550              :  *
     551              :  * @return `true` of the operation is successful, otherwise `false`.
     552              :  */
     553            1 : static inline bool timespec_negate(struct timespec *ts)
     554              : {
     555              :         __ASSERT_NO_MSG((ts != NULL) && timespec_is_valid(ts));
     556              : 
     557              : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_sub_overflow)
     558              : 
     559              :         return !__builtin_sub_overflow(0LL, ts->tv_sec, &ts->tv_sec) &&
     560              :                !__builtin_sub_overflow(0L, ts->tv_nsec, &ts->tv_nsec) && timespec_normalize(ts);
     561              : 
     562              : #else
     563              : 
     564              :         if (ts->tv_sec == SYS_TIME_T_MIN) {
     565              :                 /* -SYS_TIME_T_MIN > SYS_TIME_T_MAX, so positive integer overflow would occur */
     566              :                 return false;
     567              :         }
     568              : 
     569              :         ts->tv_sec = -ts->tv_sec;
     570              :         ts->tv_nsec = -ts->tv_nsec;
     571              : 
     572              :         return timespec_normalize(ts);
     573              : 
     574              : #endif
     575              : }
     576              : 
     577              : /**
     578              :  * @brief Subtract one timespec from another
     579              :  *
     580              :  * This function subtracts the timespec pointed to by @p b from the timespec pointed to by @p a and
     581              :  * stores the result in the timespce pointed to by @p a.
     582              :  *
     583              :  * If the operation would result in integer overflow, return value is `false`.
     584              :  *
     585              :  * @note @p a and @p b must be non-`NULL`.
     586              :  *
     587              :  * @param a the timespec which is subtracted from
     588              :  * @param b the timespec to be subtracted
     589              :  *
     590              :  * @return `true` if the operation is successful, otherwise `false`.
     591              :  */
     592            1 : static inline bool timespec_sub(struct timespec *a, const struct timespec *b)
     593              : {
     594              :         __ASSERT_NO_MSG(a != NULL);
     595              :         __ASSERT_NO_MSG(b != NULL);
     596              : 
     597              :         struct timespec neg = *b;
     598              : 
     599              :         return timespec_negate(&neg) && timespec_add(a, &neg);
     600              : }
     601              : 
     602              : /**
     603              :  * @brief Compare two timespec objects
     604              :  *
     605              :  * This function compares two timespec objects pointed to by @p a and @p b.
     606              :  *
     607              :  * @note @p a and @p b must be non-`NULL` and normalized.
     608              :  *
     609              :  * @param a the first timespec to compare
     610              :  * @param b the second timespec to compare
     611              :  *
     612              :  * @return -1, 0, or +1 if @a a is less than, equal to, or greater than @a b, respectively.
     613              :  */
     614            1 : static inline int timespec_compare(const struct timespec *a, const struct timespec *b)
     615              : {
     616              :         __ASSERT_NO_MSG((a != NULL) && timespec_is_valid(a));
     617              :         __ASSERT_NO_MSG((b != NULL) && timespec_is_valid(b));
     618              : 
     619              :         return (((a->tv_sec == b->tv_sec) && (a->tv_nsec < b->tv_nsec)) * -1) +
     620              :                (((a->tv_sec == b->tv_sec) && (a->tv_nsec > b->tv_nsec)) * 1) +
     621              :                ((a->tv_sec < b->tv_sec) * -1) + ((a->tv_sec > b->tv_sec));
     622              : }
     623              : 
     624              : /**
     625              :  * @brief Check if two timespec objects are equal
     626              :  *
     627              :  * This function checks if the two timespec objects pointed to by @p a and @p b are equal.
     628              :  *
     629              :  * @note @p a and @p b must be non-`NULL` are not required to be normalized.
     630              :  *
     631              :  * @param a the first timespec to compare
     632              :  * @param b the second timespec to compare
     633              :  *
     634              :  * @return true if the two timespec objects are equal, otherwise false.
     635              :  */
     636            1 : static inline bool timespec_equal(const struct timespec *a, const struct timespec *b)
     637              : {
     638              :         __ASSERT_NO_MSG(a != NULL);
     639              :         __ASSERT_NO_MSG(b != NULL);
     640              : 
     641              :         return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
     642              : }
     643              : 
     644              : /**
     645              :  * @}
     646              :  */
     647              : 
     648              : /**
     649              :  * @ingroup timeutil_repr_apis
     650              :  * @{
     651              :  */
     652              : 
     653              : /**
     654              :  * @brief Convert a kernel timeout to a timespec
     655              :  *
     656              :  * This function converts time durations expressed as Zephyr @ref k_timeout_t
     657              :  * objects to `struct timespec` objects.
     658              :  *
     659              :  * @note This function will assert if assertions are enabled and @p timeout is not relative,
     660              :  * (i.e. a timeout generated by `K_TIMEOUT_ABS_TICKS` or similar is used).
     661              :  *
     662              :  * @param timeout the kernel timeout to convert
     663              :  * @param[out] ts the timespec to store the result
     664              :  */
     665            1 : static inline void timespec_from_timeout(k_timeout_t timeout, struct timespec *ts)
     666              : {
     667              :         __ASSERT_NO_MSG(ts != NULL);
     668              :         __ASSERT_NO_MSG(Z_IS_TIMEOUT_RELATIVE(timeout) ||
     669              :                         (IS_ENABLED(CONFIG_TIMEOUT_64BIT) &&
     670              :                          K_TIMEOUT_EQ(timeout, (k_timeout_t){K_TICKS_FOREVER})));
     671              : 
     672              :         /* equivalent of K_FOREVER without including kernel.h */
     673              :         if (K_TIMEOUT_EQ(timeout, (k_timeout_t){K_TICKS_FOREVER})) {
     674              :                 /* duration == K_TICKS_FOREVER ticks */
     675              :                 *ts = SYS_TIMESPEC_FOREVER;
     676              :                 /* equivalent of K_NO_WAIT without including kernel.h */
     677              :         } else if (K_TIMEOUT_EQ(timeout, (k_timeout_t){0})) {
     678              :                 /* duration <= 0 ticks */
     679              :                 *ts = SYS_TIMESPEC_NO_WAIT;
     680              :         } else {
     681              :                 *ts = SYS_TICKS_TO_TIMESPEC(timeout.ticks);
     682              :         }
     683              : 
     684              :         __ASSERT_NO_MSG(timespec_is_valid(ts));
     685              : }
     686              : 
     687              : /**
     688              :  * @brief Convert a timespec to a kernel timeout
     689              :  *
     690              :  * This function converts a time duration, @p req, expressed as a `timespec` object, to a Zephyr
     691              :  * @ref k_timeout_t object.
     692              :  *
     693              :  * If @p req contains a negative duration or if both `tv_sec` and `tv_nsec` fields are zero, this
     694              :  * function will return @ref K_NO_WAIT.
     695              :  *
     696              :  * If @p req contains the maximum representable `timespec`, `{max(time_t), 999999999}`, then this
     697              :  * function will return @ref K_FOREVER.
     698              :  *
     699              :  * If @p req contains a value that is greater than the maximum equivalent tick duration, then this
     700              :  * function will return the maximum representable tick duration (i.e. @p req will be rounded-down).
     701              :  *
     702              :  * Otherwise, this function will return the `k_timeout_t` that is rounded-up to a tick boundary.
     703              :  *
     704              :  * If @p rem is not `NULL`, it will be set to the remainder of the conversion, i.e. the difference
     705              :  * between the requested duration and the converted duration as a `timespec` object, approximately
     706              :  * as shown below.
     707              :  *
     708              :  * ```python
     709              :  * rem = requested_duration - converted_duration
     710              :  * ```
     711              :  *
     712              :  * @param req the requested `timespec` to convert
     713              :  * @param[out] rem optional pointer to a `timespec` to store the remainder
     714              :  * @return the corresponding kernel timeout
     715              :  */
     716            1 : static inline k_timeout_t timespec_to_timeout(const struct timespec *req, struct timespec *rem)
     717              : {
     718              :         k_timeout_t timeout;
     719              : 
     720              :         __ASSERT_NO_MSG((req != NULL) && timespec_is_valid(req));
     721              : 
     722              :         if (timespec_compare(req, &SYS_TIMESPEC_NO_WAIT) <= 0) {
     723              :                 if (rem != NULL) {
     724              :                         *rem = *req;
     725              :                 }
     726              :                 /* equivalent of K_NO_WAIT without including kernel.h */
     727              :                 timeout.ticks = 0;
     728              :                 return timeout;
     729              :         }
     730              : 
     731              :         if (timespec_compare(req, &SYS_TIMESPEC_FOREVER) == 0) {
     732              :                 if (rem != NULL) {
     733              :                         *rem = SYS_TIMESPEC_NO_WAIT;
     734              :                 }
     735              :                 /* equivalent of K_FOREVER without including kernel.h */
     736              :                 timeout.ticks = K_TICKS_FOREVER;
     737              :                 return timeout;
     738              :         }
     739              : 
     740              :         if (timespec_compare(req, &SYS_TIMESPEC_MAX) >= 0) {
     741              :                 /* round down to align to max ticks */
     742              :                 timeout.ticks = K_TICK_MAX;
     743              :         } else {
     744              :                 /* round up to align to next tick boundary */
     745              :                 timeout.ticks = CLAMP(k_ns_to_ticks_ceil64(req->tv_nsec) +
     746              :                                               k_sec_to_ticks_ceil64(req->tv_sec),
     747              :                                       K_TICK_MIN, K_TICK_MAX);
     748              :         }
     749              : 
     750              :         if (rem != NULL) {
     751              :                 timespec_from_timeout(timeout, rem);
     752              :                 timespec_sub(rem, req);
     753              :                 timespec_negate(rem);
     754              :         }
     755              : 
     756              :         return timeout;
     757              : }
     758              : 
     759              : /**
     760              :  * @}
     761              :  */
     762              : 
     763              : #ifdef __cplusplus
     764              : }
     765              : #endif
     766              : 
     767              : #endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */
        

Generated by: LCOV version 2.0-1