diff --git a/include/zephyr/kernel.h b/include/zephyr/kernel.h index a407678bcaa4e..3ab11a4f58c16 100644 --- a/include/zephyr/kernel.h +++ b/include/zephyr/kernel.h @@ -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 diff --git a/include/zephyr/rtio/rtio.h b/include/zephyr/rtio/rtio.h index fade8e5eec19f..f145441d97197 100644 --- a/include/zephyr/rtio/rtio.h +++ b/include/zephyr/rtio/rtio.h @@ -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 */ diff --git a/subsys/rtio/rtio_sched.c b/subsys/rtio/rtio_sched.c index e117b31d45899..cb6c4e4a49f8d 100644 --- a/subsys/rtio/rtio_sched.c +++ b/subsys/rtio/rtio_sched.c @@ -7,19 +7,11 @@ #include #include -/** 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); @@ -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); } diff --git a/tests/kernel/common/CMakeLists.txt b/tests/kernel/common/CMakeLists.txt index 83d5664191660..f4cca2fe52964 100644 --- a/tests/kernel/common/CMakeLists.txt +++ b/tests/kernel/common/CMakeLists.txt @@ -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 +) diff --git a/tests/kernel/common/src/timeout_record.c b/tests/kernel/common/src/timeout_record.c new file mode 100644 index 0000000000000..782b3ee01c272 --- /dev/null +++ b/tests/kernel/common/src/timeout_record.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +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); +}