From b9aeaa741812e97d10b58d90aa54d07370aa0bd7 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Thu, 24 May 2018 14:16:53 +0200 Subject: [PATCH] sys/event: decouple event_queue from thread --- sys/event/event.c | 59 +++++++++++++++++++++++++++++++++++++-------- sys/include/event.h | 34 +++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/sys/event/event.c b/sys/event/event.c index 9e6ac213fe..19fac6d691 100644 --- a/sys/event/event.c +++ b/sys/event/event.c @@ -1,11 +1,25 @@ /* * Copyright (C) 2017 Kaspar Schleiser + * 2018 Freie Universität Berlin * * 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 + * @{ + * + * @file + * @brief Event loop implementation + * + * @author Kaspar Schleiser + * @author Hauke Petersen + * + * @} + */ + #include #include @@ -14,6 +28,12 @@ #include "clist.h" #include "thread.h" +void event_queue_init_detached(event_queue_t *queue) +{ + assert(queue); + memset(queue, '\0', sizeof(*queue)); +} + void event_queue_init(event_queue_t *queue) { assert(queue); @@ -21,17 +41,31 @@ void event_queue_init(event_queue_t *queue) queue->waiter = (thread_t *)sched_active_thread; } +void event_queue_claim(event_queue_t *queue) +{ + assert(queue && (queue->waiter == NULL)); + queue->waiter = (thread_t *)sched_active_thread; +} + void event_post(event_queue_t *queue, event_t *event) { - assert(queue && queue->waiter && event); + assert(queue && event); unsigned state = irq_disable(); if (!event->list_node.next) { clist_rpush(&queue->event_list, &event->list_node); } + thread_t *waiter = queue->waiter; irq_restore(state); - thread_flags_set(queue->waiter, THREAD_FLAG_EVENT); + /* WARNING: there is a minimal chance, that a waiter claims a formerly + * detached queue between the end of the critical section above and + * the block below. In that case, the new waiter will not be woken + * up. This should be fixed at some point once it is safe to call + * thread_flags_set() inside a critical section on all platforms. */ + if (waiter) { + thread_flags_set(waiter, THREAD_FLAG_EVENT); + } } void event_cancel(event_queue_t *queue, event_t *event) @@ -49,8 +83,8 @@ event_t *event_get(event_queue_t *queue) { unsigned state = irq_disable(); event_t *result = (event_t *) clist_lpop(&queue->event_list); - irq_restore(state); + if (result) { result->list_node.next = NULL; } @@ -59,13 +93,18 @@ event_t *event_get(event_queue_t *queue) event_t *event_wait(event_queue_t *queue) { - thread_flags_wait_any(THREAD_FLAG_EVENT); - unsigned state = irq_disable(); - event_t *result = (event_t *) clist_lpop(&queue->event_list); - if (clist_rpeek(&queue->event_list)) { - queue->waiter->flags |= THREAD_FLAG_EVENT; - } - irq_restore(state); + assert(queue); + event_t *result; + + do { + unsigned state = irq_disable(); + result = (event_t *)clist_lpop(&queue->event_list); + irq_restore(state); + if (result == NULL) { + thread_flags_wait_any(THREAD_FLAG_EVENT); + } + } while (result == NULL); + result->list_node.next = NULL; return result; } diff --git a/sys/include/event.h b/sys/include/event.h index 0727459c91..62ddda31c9 100644 --- a/sys/include/event.h +++ b/sys/include/event.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Kaspar Schleiser + * 2018 Freie Universität Berlin * * 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 @@ -16,8 +17,12 @@ * An event queue is basically a FIFO queue of events, with some functions to * efficiently and safely handle adding and getting events to / from such a * queue. + * * An event queue is bound to a thread, but any thread or ISR can put events - * into a queue. + * into a queue. In most cases, the owning thread of a queue is set during the + * queue's initialization. But it is also possible to initialize a queue in a + * detached state from a different context and to set the owning thread + * at a later point of time using the event_queue_claim() function. * * An event is a structure containing a pointer to an event handler. It can be * extended to provide context or arguments to the handler. It can also be @@ -84,6 +89,7 @@ * @brief Event API * * @author Kaspar Schleiser + * @author Hauke Petersen */ #ifndef EVENT_H @@ -111,6 +117,11 @@ extern "C" { */ #define EVENT_QUEUE_INIT { .waiter = (thread_t *)sched_active_thread } +/** + * @brief static initializer for detached event queues + */ +#define EVENT_QUEUE_INIT_DETACHED { 0 } + /** * @brief event structure forward declaration */ @@ -146,6 +157,25 @@ typedef struct { */ void event_queue_init(event_queue_t *queue); +/** + * @brief Initialize an event queue not binding it to a thread + * + * @param[out] queue event queue object to initialize + */ +void event_queue_init_detached(event_queue_t *queue); + +/** + * @brief Bind an event queue to the calling thread + * + * This function must only be called once and only if the given queue is not + * yet bound to a thread. + * + * @pre (queue->waiter == NULL) + * + * @param[out] queue event queue object to bind to a thread + */ +void event_queue_claim(event_queue_t *queue); + /** * @brief Queue an event * @@ -192,6 +222,8 @@ 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! + * * @param[in] queue event queue to get event from * * @returns pointer to next event