Skip to content

Make kernel timeout record (struct _timeout) a public API #91146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions include/zephyr/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -6513,6 +6513,92 @@ void k_sys_runtime_stats_enable(void);
*/
void k_sys_runtime_stats_disable(void);

struct k_timeout_record;

/**
* @brief Kernel timeout record elapsed callback function template.
*
* @param record Address of kernel timeout record.
*/
typedef void (*k_timeout_record_fn_t)(struct k_timeout_record *record);

/**
* @cond INTERNAL_HIDDEN
*/

struct k_timeout_record {
struct _timeout to;
};

k_ticks_t z_add_timeout(struct _timeout *to, _timeout_func_t fn, k_timeout_t timeout);
int z_abort_timeout(struct _timeout *to);

/**
* INTERNAL_HIDDEN @endcond
*/

#ifdef CONFIG_SYS_CLOCK_EXISTS

/** Initialize a kernel timeout record */
static inline void k_timeout_record_init(struct k_timeout_record *record)
{
sys_dnode_init(&record->to.node);
}

/**
* @brief Add a kernel timeout record.
*
* @details The kernel timeout record is the most primitive timeout
* in the kernel. @p fn is called only once once the timeout elapses,
* at which point the kernel timeout record can be re-added.
*
* @warning @p fn is likely called from ISR context.
*
* @warning Timeout record can only be re-added once it has elapsed
* or has been aborted.
*
* @note The timeout is called at the next kernel tick if timeout
* is in the past or now.
*
* @see k_timeout_record_abort()
*
* @param record The timeout record structure.
* @param fn The function called when timeout elapses.
* @param timeout The time at which timeout elapses.
*
* @retval Absolute ticks at which timeout elapses.
*/
static inline k_ticks_t k_timeout_record_add(struct k_timeout_record *record,
k_timeout_record_fn_t fn,
k_timeout_t timeout)
{
return z_add_timeout(&record->to, (_timeout_func_t)fn, timeout);
}

/**
* @brief Abort a kernel timeout record.
*
* @details Abort a kernel timeout record and check if the
* kernel timeout record's timeout elapsed.
*
* @note This function must be called before re-adding the
* kernel timeout record if caller is unsure whether the
* timeout has elapsed.
*
* @see k_timeout_record_add()
*
* @param record The timeout record structure.
*
* @retval true if timeout record has not elapsed.
* @retval false if timeout record has elapsed or was never added.
*/
static inline bool k_timeout_record_abort(struct k_timeout_record *record)
{
return z_abort_timeout(&record->to) == 0;
}

#endif /* CONFIG_SYS_CLOCK_EXISTS */

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion include/zephyr/rtio/rtio.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ struct rtio_sqe {
/** OP_DELAY */
struct {
k_timeout_t timeout; /**< Delay timeout. */
struct _timeout to; /**< Timeout struct. Used internally. */
struct k_timeout_record record; /**< Timeout record. Used internally. */
} delay;

/** OP_I2C_CONFIGURE */
Expand Down
16 changes: 4 additions & 12 deletions subsys/rtio/rtio_sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,11 @@
#include <zephyr/kernel.h>
#include <zephyr/rtio/rtio.h>

/** Required to access Timeout Queue APIs, which are used instead of the
* Timer APIs because of concerns on size on rtio_sqe (k_timer is more
* than double the size of _timeout). Users will have to instantiate a
* pool of SQE objects, thus its size directly impacts memory footprint
* of RTIO applications.
*/
#include <../kernel/include/timeout_q.h>

#include "rtio_sched.h"

static void rtio_sched_alarm_expired(struct _timeout *t)
static void rtio_sched_alarm_expired(struct k_timeout_record *record)
{
struct rtio_sqe *sqe = CONTAINER_OF(t, struct rtio_sqe, delay.to);
struct rtio_sqe *sqe = CONTAINER_OF(record, struct rtio_sqe, delay.record);
struct rtio_iodev_sqe *iodev_sqe = CONTAINER_OF(sqe, struct rtio_iodev_sqe, sqe);

rtio_iodev_sqe_ok(iodev_sqe, 0);
Expand All @@ -29,6 +21,6 @@ void rtio_sched_alarm(struct rtio_iodev_sqe *iodev_sqe, k_timeout_t timeout)
{
struct rtio_sqe *sqe = &iodev_sqe->sqe;

z_init_timeout(&sqe->delay.to);
z_add_timeout(&sqe->delay.to, rtio_sched_alarm_expired, timeout);
k_timeout_record_init(&sqe->delay.record);
k_timeout_record_add(&sqe->delay.record, rtio_sched_alarm_expired, timeout);
}
6 changes: 6 additions & 0 deletions tests/kernel/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ target_sources_ifdef(
app PRIVATE
src/irq_offload.c
)

target_sources_ifdef(
CONFIG_SYS_CLOCK_EXISTS
app PRIVATE
src/timeout_record.c
)
49 changes: 49 additions & 0 deletions tests/kernel/common/src/timeout_record.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/ztest.h>

static K_SEM_DEFINE(test_sem, 0, 1);
static struct k_timeout_record *test_record;

extern void *common_setup(void);

static void test_before(void *f)
{
k_sem_reset(&test_sem);
test_record = NULL;
}

ZTEST_SUITE(timeout_record, NULL, common_setup, test_before, NULL, NULL);

static void test_timeout_handler(struct k_timeout_record *record)
{
test_record = record;
k_sem_give(&test_sem);
}

ZTEST(timeout_record, test_timeout_add_elapse_abort)
{
struct k_timeout_record record;

k_timeout_record_init(&record);
(void)k_timeout_record_add(&record, test_timeout_handler, K_NO_WAIT);
zassert_ok(k_sem_take(&test_sem, K_MSEC(100)));
zassert_equal(&record, test_record);
zassert_false(k_timeout_record_abort(&record));
}

ZTEST(timeout_record, test_timeout_add_abort)
{
struct k_timeout_record record;

k_timeout_record_init(&record);
(void)k_timeout_record_add(&record, test_timeout_handler, K_MSEC(1000));
zassert_equal(k_sem_take(&test_sem, K_MSEC(500)), -EAGAIN);
zassert_true(k_timeout_record_abort(&record));
zassert_equal(k_sem_take(&test_sem, K_MSEC(1000)), -EAGAIN);
}
Loading