diff --git a/sys/include/event/deferred_callback.h b/sys/include/event/deferred_callback.h new file mode 100644 index 0000000000..6cc95adbca --- /dev/null +++ b/sys/include/event/deferred_callback.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2025 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_event + * @brief Trigger an event callback after a timeout + * + * This provides convenience functions to trigger a callback event after + * some time has passed. + * + * @{ + * + * @file + * @brief Event Deferred Callback API + * + * @author Benjamin Valentin + * + */ + +#ifndef EVENT_DEFERRED_CALLBACK_H +#define EVENT_DEFERRED_CALLBACK_H + +#include +#include "event/callback.h" +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Deferred Callback Event structure + */ +typedef struct { + event_callback_t event; /**< callback event portion */ + event_queue_t *queue; /**< event queue to post event to */ + ztimer_t timer; /**< ztimer object used for timeout */ +} event_deferred_callback_t; + +/** + * @brief Internal helper function for ztimer callback + * @param[in] arg event structure + */ +static inline void _event_deferred_post(void *arg) +{ + event_deferred_callback_t *event_timeout = arg; + + event_post(event_timeout->queue, arg); +} + +/** + * @brief Execute a callback function in the event thread after a timeout + * + * @warning @p event must be kept allocated until the callback was executed + * + * @param[in] event event_deferred_callback object to initialize + * @param[in] queue queue that the timed-out event will be added to + * @param[in] clock the clock to configure this timer on + * @param[in] timeout timer target (relative ticks from now) + * @param[in] callback callback to set up + * @param[in] arg callback argument to set up + */ +static inline void event_deferred_callback_post(event_deferred_callback_t *event, + event_queue_t *queue, + ztimer_clock_t *clock, uint32_t timeout, + void (*callback)(void *), void *arg) +{ + event->event = (event_callback_t) { + .super.handler = _event_callback_handler, + .callback = callback, + .arg = arg, + }; + event->timer = (ztimer_t) { + .callback = _event_deferred_post, + .arg = event, + }; + event->queue = queue; + + ztimer_set(clock, &event->timer, timeout); +} + +/** + * @brief Cancel a callback function if it has not been executed yet + * + * @param[in] event event_deferred_callback object to cancel + * @param[in] clock the clock this timer runs on + */ +static inline void event_deferred_callback_cancel(event_deferred_callback_t *event, + ztimer_clock_t *clock) +{ + ztimer_remove(clock, &event->timer); + event_cancel(event->queue, (event_t *)event); +} + +#ifdef __cplusplus +} +#endif +#endif /* EVENT_DEFERRED_CALLBACK_H */ +/** @} */ diff --git a/tests/sys/event_periodic_callback/main.c b/tests/sys/event_periodic_callback/main.c index 7f42995ea9..dc3eabf689 100644 --- a/tests/sys/event_periodic_callback/main.c +++ b/tests/sys/event_periodic_callback/main.c @@ -20,6 +20,7 @@ #include +#include "event/deferred_callback.h" #include "event/periodic_callback.h" #include "event/thread.h" @@ -30,11 +31,13 @@ static void _event_cb(void *ctx) int main(void) { - event_periodic_callback_t a, b, c; - event_callback_t oneshot; event_callback_oneshot(&oneshot, EVENT_PRIO_MEDIUM, _event_cb, "event 0"); + event_deferred_callback_t d; + event_deferred_callback_post(&d, EVENT_PRIO_MEDIUM, ZTIMER_MSEC, 750, _event_cb, "event D"); + + event_periodic_callback_t a, b, c; event_periodic_callback_init(&a, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, _event_cb, "event A"); event_periodic_callback_init(&b, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, _event_cb, "event B"); event_periodic_callback_init(&c, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, _event_cb, "event C"); diff --git a/tests/sys/event_periodic_callback/tests/01-run.py b/tests/sys/event_periodic_callback/tests/01-run.py index 2bfbf1b417..7912e60dc6 100755 --- a/tests/sys/event_periodic_callback/tests/01-run.py +++ b/tests/sys/event_periodic_callback/tests/01-run.py @@ -7,6 +7,7 @@ from testrunner import run def testfunc(child): child.expect_exact("event 0") child.expect_exact("event A") + child.expect_exact("event D") child.expect_exact("event B") child.expect_exact("event A") child.expect_exact("event C")