1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #17614 from chrysn-pull-requests/ztimer-doc-stricter

sys/ztimer doc: List prerequisites for successful use of ztimer_now
This commit is contained in:
chrysn 2022-02-15 09:09:12 +01:00 committed by GitHub
commit 69db27f1cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -474,8 +474,103 @@ ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock);
/**
* @brief Get the current time from a clock
*
* @warning don't compare ztimer_now() values from different clocks. The
* clocks are almost certainly not synchronized.
* There are several caveats to consider when using values returned by
* `ztimer_now()` (or comparing those values to results of @ref ztimer_set,
* which are compatible unless MODULE_ZTMIER_NOW64 is in use):
*
* * A single value has no meaning of its own. Meaningful results are only ever
* produced when subtracting values from each other (in the wrapping fashion
* implied by the use of unsigned integers in C).
*
* For example, even though it may be the case in some scenarios, the value
* does **not** indicate time since system startup.
*
* * Only values obtained from the same clock can be compared.
*
* * Two values can only be compared when the clock has been continuously
* active between the first and the second reading.
*
* A clock is guaranteed to be active from the time any timer is set (the
* first opportunity to get a "now" value from it is the return value of @ref
* ztimer_set) until the time the timer's callback returns. The clock also
* stays active when timers are set back-to-back (which is the case when the
* first timer's callback sets the second timer), or when they overlap (which
* can be known by starting the second timer and afterwards observing that
* @ref ztimer_is_set or @ref ztimer_remove returns true in a low-priority
* context).
*
* In contrast, the clock is not guaranteed to be active if a timer is
* removed and then a second one is started (even if the thread does not
* block between these events), or when an expiring timer wakes up a thread
* that then sets the second timer.
*
* If the clock was active, then the difference between the second value and
* the first is then the elapsed time in the clock's unit, **modulo 2³²
* ticks** (or 2 when using the ZTIMER_NOW64 module).
*
* * A difference between two values (calculated in the usual wrapping way) is
* guaranteed to be exactly the elapsed time (not just modulo 2³²) if there
* exists a single timer that is continuously set while both
* readings are taken (which in particular means that the clock was
* continuously active), **and** the timer is observed to be still set when
* after the second reading an execution context with lower priority than the
* ZTimer interrupt has run. (In particular, this is the case in a thread
* context when interrupts are enabled).
*
* For example, this sequence of events will return usable values:
*
* * In a thread, a timer is set.
* * Some interrupt fires, and `start = ztimer_now(ZTIMER_MSEC)` is set in
* the handler.
* * The interrupt fires again, and `duration = start -
* ztimer_now(ZTIMER_MSEC)` is stored.
* * Back in the thread context, @ref ztimer_remove on the timer returns
* true.
*
* Only now, `duration` can be known to be a duration in milliseconds.
*
* (By comparison, if the timer were removed right inside the second
* interrupt, then duration might either be correct, or it might be 5
* milliseconds when really 2³² + 5 milliseconds have elapsed)
*
* The requirement of the execution contexts can be **dispensed with, if**
* the set timer is shorter than the wrap-around time of the clock by at
* least the maximum duration the full system is allowed to spend between
* interrupt servicing opportunities. That time varies by setup, but an
* upper bound of 1 minute is conservative enough for system modules to use.
*
* For example, this sequence of events will also return usable values:
*
* * A mutex is locked, and a timer is set to unlock it on the millisecond
* timer after 1 hour. (This is way less than the wrap-around time of
* around 50 days).
* * The return value of setting the timer is noted as start time.
* * Some interrupt fires, and `ztimer_now()` is taken. Then (still inside
* the ISR), @ref mutex_trylock is used to test for whether the interrupt
* is still locked (indicating that the timer has not been processed). If
* locking failed, the difference is valid and can be used immediately.
* Otherwise, the mutex needs to be freed again, and the difference is
* discarded (it can be stored as "longer than 1 hour").
*
* * To compare two values T1 and T2 without additional knowledge (eg. of a
* maximum time difference between them), it has to be known which value was
* read earlier, so that the earlier can be subtracted from the later.
*
* If that is not known, an easy solution is to store a base value T0 inside
* the same single-timer window as T1 and T2, and then compare (T2 - T0) and
* (T1 - T0) to see which of the events occurred earlier.
*
* The above criteria are conservative API guarantees of `ztimer_now`. There
* can be additional properties of a system that allow additional usage
* patterns; these need to be evaluated case-by-case. (For example, a ZTimer
* backed by a timer that never stops might be comparable even without a
* running timer.)
*
* @warning All the above need to be considered before using the results of
* this function. Not considering them may give results that appear to
* be valid, but that can change without prior warning, e.g. when
* unrelated components are altered that change the systems's power
* management behavior.
*
* @param[in] clock ztimer clock to operate on
*