Skip to content

Commit

Permalink
doc: kernel: clocks: define "current time"
Browse files Browse the repository at this point in the history
Scheduling relative timeouts from within timer callbacks (=sys clock ISR
context) differs from scheduling relative timeouts from an application
context.

This change documents and explains the rationale of this distinction.

Signed-off-by: Florian Grandel <[email protected]>
  • Loading branch information
fgrandel authored and carlescufi committed Jun 30, 2023
1 parent ac95e57 commit 5fa5534
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
20 changes: 17 additions & 3 deletions doc/kernel/services/timing/clocks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,23 @@ For example:

All these values are specified using a :c:struct:`k_timeout_t` value. This is
an opaque struct type that must be initialized using one of a family
of kernel timeout macros. The most common, :c:macro:`K_MSEC` , defines
a time in milliseconds after the current time (strictly: the time at
which the kernel receives the timeout value).
of kernel timeout macros. The most common, :c:macro:`K_MSEC`, defines
a time in milliseconds after the current time.

What is meant by "current time" for relative timeouts depends on the context:

* When scheduling a relative timeout from within a timeout callback (e.g. from
within the expiry function passed to :c:func:`k_timer_init` or the work handler
passed to :c:func:`k_work_init_delayable`), "current time" is the exact time at
which the currently firing timeout was originally scheduled even if the "real
time" will already have advanced. This is to ensure that timers scheduled from
within another timer's callback will always be calculated with a precise offset
to the firing timer. It is thereby possible to fire at regular intervals without
introducing systematic clock drift over time.

* When scheduling a timeout from application context, "current time" means the
value returned by :c:func:`k_uptime_ticks` at the time at which the kernel
receives the timeout value.

Other options for timeout initialization follow the unit conventions
described above: :c:macro:`K_NSEC()`, :c:macro:`K_USEC`, :c:macro:`K_TICKS` and
Expand Down
18 changes: 17 additions & 1 deletion kernel/timeout.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static struct k_spinlock timeout_lock;
#define MAX_WAIT (IS_ENABLED(CONFIG_SYSTEM_CLOCK_SLOPPY_IDLE) \
? K_TICKS_FOREVER : INT_MAX)

/* Cycles left to process in the currently-executing sys_clock_announce() */
/* Ticks left to process in the currently-executing sys_clock_announce() */
static int announce_remaining;

#if defined(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)
Expand Down Expand Up @@ -61,6 +61,22 @@ static void remove_timeout(struct _timeout *t)

static int32_t elapsed(void)
{
/* While sys_clock_announce() is executing, new relative timeouts will be
* scheduled relatively to the currently firing timeout's original tick
* value (=curr_tick) rather than relative to the current
* sys_clock_elapsed().
*
* This means that timeouts being scheduled from within timeout callbacks
* will be scheduled at well-defined offsets from the currently firing
* timeout.
*
* As a side effect, the same will happen if an ISR with higher priority
* preempts a timeout callback and schedules a timeout.
*
* The distinction is implemented by looking at announce_remaining which
* will be non-zero while sys_clock_announce() is executing and zero
* otherwise.
*/
return announce_remaining == 0 ? sys_clock_elapsed() : 0U;
}

Expand Down

0 comments on commit 5fa5534

Please sign in to comment.