diff --git a/sys/event/event.c b/sys/event/event.c index 56c1ae5b94..2091cf9199 100644 --- a/sys/event/event.c +++ b/sys/event/event.c @@ -96,14 +96,20 @@ event_t *event_get(event_queue_t *queue) return result; } -event_t *event_wait(event_queue_t *queue) +event_t *event_wait_multi(event_queue_t *queues, size_t n_queues) { - assert(queue); + assert(queues && n_queues); event_t *result; do { unsigned state = irq_disable(); - result = (event_t *)clist_lpop(&queue->event_list); + for (size_t i = 0; i < n_queues; i++) { + result = container_of(clist_lpop(&queues[i].event_list), + event_t, list_node); + if (result) { + break; + } + } irq_restore(state); if (result == NULL) { thread_flags_wait_any(THREAD_FLAG_EVENT); @@ -152,11 +158,11 @@ event_t *event_wait_timeout64(event_queue_t *queue, uint64_t timeout) } #endif -void event_loop(event_queue_t *queue) +void event_loop_multi(event_queue_t *queues, size_t n_queues) { event_t *event; - while ((event = event_wait(queue))) { + while ((event = event_wait_multi(queues, n_queues))) { event->handler(event); } } diff --git a/sys/include/event.h b/sys/include/event.h index 2814a16347..09ef7ea291 100644 --- a/sys/include/event.h +++ b/sys/include/event.h @@ -215,6 +215,35 @@ void event_cancel(event_queue_t *queue, event_t *event); */ event_t *event_get(event_queue_t *queue); +/** + * @brief Get next event from the given event queues, blocking + * + * This function will block until an event becomes available. If more than one + * queue contains an event, the queue with the lowest index is chosen. Thus, + * a lower index in the @p queues array translates into a higher priority of + * the queue. + * + * In order to handle an event retrieved using this function, + * call event->handler(event). + * + * @warning There can only be a single waiter on a queue! + * + * @note This function *can* be suitable for having a single thread + * handling both real-time and non-real-time events. However, a real + * time event can be delayed for the whole duration a single + * non-real-time event takes (in addition to all other sources of + * latency). Thus, the slowest to handle non-real-time event must still + * execute fast enough to add an amount of latency (on top of other + * sources of latency) that is acceptable to the real-time event with + * the strictest requirements. + * + * @param[in] queues Array of event queues to get event from + * @param[in] n_queues Number of event queues passed in @p queues + * + * @returns pointer to next event + */ +event_t *event_wait_multi(event_queue_t *queues, size_t n_queues); + /** * @brief Get next event from event queue, blocking * @@ -223,13 +252,16 @@ event_t *event_get(event_queue_t *queue); * In order to handle an event retrieved using this function, * call event->handler(event). * - * @note There can only be a single waiter on a queue! + * @warning There can only be a single waiter on a queue! * * @param[in] queue event queue to get event from * * @returns pointer to next event */ -event_t *event_wait(event_queue_t *queue); +static inline event_t *event_wait(event_queue_t *queue) +{ + return event_wait_multi(queue, 1); +} #if defined(MODULE_XTIMER) || defined(DOXYGEN) /** @@ -255,6 +287,29 @@ event_t *event_wait_timeout(event_queue_t *queue, uint32_t timeout); event_t *event_wait_timeout64(event_queue_t *queue, uint64_t timeout); #endif +/** + * @brief Simple event loop with multiple queues + * + * This function will forever sit in a loop, waiting for events to be queued + * and executing their handlers. If more than one queue contains an event, the + * queue with the lowest index is chosen. Thus, a lower index in the @p queues + * array translates into a higher priority of the queue. + * + * It is pretty much defined as: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * while ((event = event_wait_multi(queues, n_queues))) { + * event->handler(event); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see event_wait_multi + * + * @param[in] queues Event queues to process + * @param[in] n_queues Number of queues passed with @p queues + */ +void event_loop_multi(event_queue_t *queues, size_t n_queues); + /** * @brief Simple event loop * @@ -271,7 +326,10 @@ event_t *event_wait_timeout64(event_queue_t *queue, uint64_t timeout); * * @param[in] queue event queue to process */ -void event_loop(event_queue_t *queue); +static inline void event_loop(event_queue_t *queue) +{ + event_loop_multi(queue, 1); +} #ifdef __cplusplus } diff --git a/tests/events/main.c b/tests/events/main.c index 7acbe9c039..2b1a85c382 100644 --- a/tests/events/main.c +++ b/tests/events/main.c @@ -28,8 +28,9 @@ #include "event/timeout.h" #include "event/callback.h" -#define STACKSIZE THREAD_STACKSIZE_DEFAULT -#define PRIO (THREAD_PRIORITY_MAIN - 1) +#define STACKSIZE THREAD_STACKSIZE_DEFAULT +#define PRIO (THREAD_PRIORITY_MAIN - 1) +#define DELAYED_QUEUES_NUMOF 2 static char stack[STACKSIZE]; @@ -42,16 +43,18 @@ static void timed_callback(void *arg); static void forbidden_callback(void *arg); static void delayed_callback1(event_t *arg); static void delayed_callback2(event_t *arg); +static void delayed_callback3(event_t *arg); static event_t event = { .handler = callback }; static event_t event2 = { .handler = callback }; static event_t delayed_event1 = { .handler = delayed_callback1 }; static event_t delayed_event2 = { .handler = delayed_callback2 }; +static event_t delayed_event3 = { .handler = delayed_callback3 }; static void callback(event_t *arg) { order++; - expect(order == 3); + expect(order == 4); expect(arg == &event); printf("triggered 0x%08x\n", (unsigned)arg); } @@ -68,7 +71,7 @@ static event_callback_t noevent_callback = EVENT_CALLBACK_INIT(forbidden_callbac static void custom_callback(event_t *event) { order++; - expect(order == 4); + expect(order == 5); expect(event == (event_t *)&custom_event); custom_event_t *custom_event = (custom_event_t *)event; printf("triggered custom event with text: \"%s\"\n", custom_event->text); @@ -77,7 +80,7 @@ static void custom_callback(event_t *event) static void timed_callback(void *arg) { order++; - expect(order == 5); + expect(order == 6); expect(arg == event_callback.arg); uint32_t now = xtimer_now_usec(); expect((now - before >= 100000LU)); @@ -99,7 +102,7 @@ static void forbidden_callback(void *arg) static void delayed_callback1(event_t *arg) { order++; - expect(order == 1); + expect(order == 2); expect(arg == &delayed_event1); printf("triggered delayed event %p\n", (void *)arg); } @@ -107,19 +110,29 @@ static void delayed_callback1(event_t *arg) static void delayed_callback2(event_t *arg) { order++; - expect(order == 2); + expect(order == 3); expect(arg == &delayed_event2); printf("triggered delayed event %p\n", (void *)arg); } +static void delayed_callback3(event_t *arg) +{ + order++; + expect(order == 1); + expect(arg == &delayed_event3); + printf("triggered delayed event %p\n", (void *)arg); +} + static void *claiming_thread(void *arg) { - event_queue_t *dq = (event_queue_t *)arg; + event_queue_t *dqs = arg; - printf("claiming event queue %p\n", (void *)dq); - event_queue_claim(dq); - printf("launching event queue %p\n", (void *)dq); - event_loop(dq); + printf("claiming event queues %p\n", (void *)dqs); + for (size_t i = 0; i < DELAYED_QUEUES_NUMOF; i++) { + event_queue_claim(&dqs[i]); + } + printf("launching event queue for queues %p\n", (void *)dqs); + event_loop_multi(dqs, DELAYED_QUEUES_NUMOF); return NULL; } @@ -129,17 +142,22 @@ int main(void) puts("[START] event test application.\n"); /* test creation of delayed claiming of a detached event queue */ - event_queue_t dq = EVENT_QUEUE_INIT_DETACHED; - printf("initializing detached event queue %p\n", (void *)&dq); - event_queue_init_detached(&dq); + event_queue_t dqs[DELAYED_QUEUES_NUMOF] = { + EVENT_QUEUE_INIT_DETACHED, EVENT_QUEUE_INIT_DETACHED + }; + printf("initializing detached event queues %p\n", (void *)dqs); + event_queue_init_detached(&dqs[0]); + event_queue_init_detached(&dqs[1]); - printf("posting %p\n", (void *)&delayed_event1); - event_post(&dq, &delayed_event1); - printf("posting %p\n", (void *)&delayed_event2); - event_post(&dq, &delayed_event2); + printf("posting %p to delayed queue at index 1\n", (void *)&delayed_event1); + event_post(&dqs[1], &delayed_event1); + printf("posting %p to delayed queue at index 1\n", (void *)&delayed_event2); + event_post(&dqs[1], &delayed_event2); + printf("posting %p to delayed queue at index 0\n", (void *)&delayed_event3); + event_post(&dqs[0], &delayed_event3); - printf("running thread that will claim event queue %p\n", (void *)&dq); - thread_create(stack, sizeof(stack), PRIO, 0, claiming_thread, &dq, "ct"); + printf("running thread that will claim event queues %p\n", (void *)&dqs); + thread_create(stack, sizeof(stack), PRIO, 0, claiming_thread, dqs, "ct"); /* test posting different kind of events in order to a statically * initialized queue */