diff --git a/sys/event/thread.c b/sys/event/thread.c index 999ff1371e..2c192620b2 100644 --- a/sys/event/thread.c +++ b/sys/event/thread.c @@ -21,21 +21,31 @@ * @} */ +#include "architecture.h" #include "thread.h" #include "event.h" #include "event/thread.h" -static void *_handler(void *event_queue) +struct event_queue_and_size { + event_queue_t *q; + size_t q_numof; +}; + +static void *_handler_thread(void *tagged_ptr) { - event_queue_claim(event_queue); - event_loop(event_queue); + event_queue_t *qs = ptrtag_ptr(tagged_ptr); + /* number of queues is encoded in lower pointer bits */ + size_t n = ptrtag_tag(tagged_ptr) + 1; + event_queues_claim(qs, n); + /* start event loop */ + event_loop_multi(qs, n); /* should be never reached */ return NULL; } -void event_thread_init(event_queue_t *queue, char *stack, size_t stack_size, - unsigned priority) +void event_thread_init_multi(event_queue_t *queues, size_t queues_numof, + char *stack, size_t stack_size, unsigned priority) { /* For the auto_init use case, this will be called before main gets * started. main might already use the queues, so they need to be @@ -43,9 +53,12 @@ void event_thread_init(event_queue_t *queue, char *stack, size_t stack_size, * * They will be claimed within the handler thread. */ - event_queue_init_detached(queue); + event_queues_init_detached(queues, queues_numof); - thread_create(stack, stack_size, priority, 0, _handler, queue, "event"); + void *tagged_ptr = ptrtag(queues, queues_numof - 1); + + thread_create(stack, stack_size, priority, 0, _handler_thread, tagged_ptr, + "event"); } #ifndef EVENT_THREAD_STACKSIZE_DEFAULT @@ -78,48 +91,43 @@ void event_thread_init(event_queue_t *queue, char *stack, size_t stack_size, #define EVENT_THREAD_LOWEST_PRIO (THREAD_PRIORITY_IDLE - 1) #endif -#ifdef MODULE_EVENT_THREAD_HIGHEST -event_queue_t event_queue_highest; -static char _evq_highest_stack[EVENT_THREAD_HIGHEST_STACKSIZE]; -#endif +/* rely on compiler / linker to garbage collect unused stacks */ +static char WORD_ALIGNED _evq_highest_stack[EVENT_THREAD_HIGHEST_STACKSIZE]; +static char WORD_ALIGNED _evq_medium_stack[EVENT_THREAD_MEDIUM_STACKSIZE]; +static char WORD_ALIGNED _evq_lowest_stack[EVENT_THREAD_LOWEST_STACKSIZE]; -#ifdef MODULE_EVENT_THREAD_MEDIUM -event_queue_t event_queue_medium; -static char _evq_medium_stack[EVENT_THREAD_MEDIUM_STACKSIZE]; -#endif - -#ifdef MODULE_EVENT_THREAD_LOWEST -event_queue_t event_queue_lowest; -static char _evq_lowest_stack[EVENT_THREAD_LOWEST_STACKSIZE]; -#endif - -typedef struct { - event_queue_t *queue; - char *stack; - size_t stack_size; - unsigned priority; -} event_threads_t; - -const event_threads_t _event_threads[] = { -#ifdef MODULE_EVENT_THREAD_HIGHEST - { &event_queue_highest, _evq_highest_stack, sizeof(_evq_highest_stack), - EVENT_THREAD_HIGHEST_PRIO }, -#endif -#ifdef MODULE_EVENT_THREAD_MEDIUM - { &event_queue_medium, _evq_medium_stack, sizeof(_evq_medium_stack), - EVENT_THREAD_MEDIUM_PRIO }, -#endif -#ifdef MODULE_EVENT_THREAD_LOWEST - { &event_queue_lowest, _evq_lowest_stack, sizeof(_evq_lowest_stack), - EVENT_THREAD_LOWEST_PRIO }, -#endif -}; +event_queue_t event_thread_queues[EVENT_QUEUE_PRIO_NUMOF]; void auto_init_event_thread(void) { - for (unsigned i = 0; i < ARRAY_SIZE(_event_threads); i++) { - event_thread_init(_event_threads[i].queue, - _event_threads[i].stack, _event_threads[i].stack_size, - _event_threads[i].priority); + if (IS_USED(MODULE_EVENT_THREAD_HIGHEST)) { + /* In order to allow highest priority events to preempt all others, + * high priority events must be run in their own thread. This thread + * can preempt than preempt the other event thread(s). */ + event_thread_init(EVENT_PRIO_HIGHEST, + _evq_highest_stack, sizeof(_evq_highest_stack), + EVENT_THREAD_HIGHEST_PRIO); } + if (IS_USED(MODULE_EVENT_THREAD_MEDIUM)) { + /* In order to allow medium priority events to preempt low priority + * events, we need to move the low priority events into their own + * thread. The always existing medium priority event thread can then + * preempt the lowest priority event thread. */ + event_thread_init(EVENT_PRIO_LOWEST, + _evq_lowest_stack, sizeof(_evq_lowest_stack), + EVENT_THREAD_LOWEST_PRIO); + } + + event_queue_t *qs = EVENT_PRIO_MEDIUM; + size_t qs_numof = 1; + if (!IS_USED(MODULE_EVENT_THREAD_HIGHEST)) { + qs = EVENT_PRIO_HIGHEST; + qs_numof = 2; + } + if (!IS_USED(MODULE_EVENT_THREAD_MEDIUM)) { + qs_numof++; + } + event_thread_init_multi(qs, qs_numof, + _evq_medium_stack, sizeof(_evq_medium_stack), + EVENT_THREAD_MEDIUM_PRIO); } diff --git a/sys/include/event.h b/sys/include/event.h index fb06d27f19..01ec3966fd 100644 --- a/sys/include/event.h +++ b/sys/include/event.h @@ -104,6 +104,7 @@ #include "irq.h" #include "thread.h" #include "thread_flags.h" +#include "ptrtag.h" #ifdef __cplusplus extern "C" { @@ -147,12 +148,11 @@ struct event { /** * @brief event queue structure */ -typedef struct { +typedef struct PTRTAG { clist_node_t event_list; /**< list of queued events */ - thread_t *waiter; /**< thread ownning event queue */ + thread_t *waiter; /**< thread owning event queue */ } event_queue_t; - /** * @brief Initialize an array of event queues * diff --git a/sys/include/event/thread.h b/sys/include/event/thread.h index 251ee92ce3..940553c576 100644 --- a/sys/include/event/thread.h +++ b/sys/include/event/thread.h @@ -12,6 +12,39 @@ * @ingroup sys_event * @brief Provides utility functions for event handler threads * + * Usage + * ===== + * + * By using the module `event_thread`, the event queues + * @ref EVENT_PRIO_HIGHEST, @ref EVENT_PRIO_MEDIUM, and + * @ref EVENT_PRIO_LOWEST are provided and declared in the header + * `event/thread.h`. With default settings, the `auto_init` module will + * automatically start one or more threads to handle these + * queues. + * + * By default, a single thread with priority `EVENT_THREAD_MEDIUM_PRIO` + * will handle all three event queues according to their priority. + * An already started event handler will not be preempted in this case by an + * incoming higher priority event. Still, lower priority event queues will only + * be worked on once the higher priority queues are empty. Hence, the worst case + * latency is increased by the worst case duration of any possible lower + * priority event in this configuration. + * + * By using module `event_thread_highest`, the highest priority queue gets its + * own thread. With this, events of the highest priority can preempt already + * running event handlers of medium and lowest priority. + * + * By using module `event_thread_medium`, the lowest priority events are handled + * in their own thread. With this, events of at least medium priority can + * preempt already running events of the lowest priority. + * + * By using both module `event_thread_highest` and `event_thread_medium`, each + * event queue gets its own thread. So higher priority events will always + * preempt events of lower priority in this case. + * + * Finally, the module `event_thread_lowest` is provided for backward + * compatibility and has no effect. + * * @{ * * @file @@ -31,6 +64,22 @@ extern "C" { #endif +/** + * @brief Convenience function for initializing an event queue thread + * handling multiple queues + * + * @param[in] queues array of the preallocated queue objects + * @param[in] queues_numof number of elements in @p queues + * @param[in] stack ptr to stack space + * @param[in] stack_size size of stack + * @param[in] priority priority to use + * + * @pre @p queues_numof is at most 4 + */ +void event_thread_init_multi(event_queue_t *queues, size_t queues_numof, + char *stack, size_t stack_size, + unsigned priority); + /** * @brief Convenience function for initializing an event queue thread * @@ -39,23 +88,41 @@ extern "C" { * @param[in] stack_size size of stack * @param[in] priority priority to use */ -void event_thread_init(event_queue_t *queue, char *stack, size_t stack_size, - unsigned priority); +static inline void event_thread_init(event_queue_t *queue, + char *stack, size_t stack_size, + unsigned priority) +{ + event_thread_init_multi(queue, 1, stack, stack_size, priority); +} -#ifdef MODULE_EVENT_THREAD_HIGHEST -extern event_queue_t event_queue_highest; -#define EVENT_PRIO_HIGHEST (&event_queue_highest) -#endif +/** + * @brief Event queue priorities + * + * @details The lower the numeric value, the higher the priority. The highest + * priority is 0, so that these priorities can be used as index to + * access arrays. + */ +enum { + EVENT_QUEUE_PRIO_HIGHEST, /**< Highest event queue priority */ + EVENT_QUEUE_PRIO_MEDIUM, /**< Medium event queue priority */ + EVENT_QUEUE_PRIO_LOWEST, /**< Lowest event queue priority */ + EVENT_QUEUE_PRIO_NUMOF /**< Number of event queue priorities */ +}; -#ifdef MODULE_EVENT_THREAD_MEDIUM -extern event_queue_t event_queue_medium; -#define EVENT_PRIO_MEDIUM (&event_queue_medium) -#endif +extern event_queue_t event_thread_queues[EVENT_QUEUE_PRIO_NUMOF]; -#ifdef MODULE_EVENT_THREAD_LOWEST -extern event_queue_t event_queue_lowest; -#define EVENT_PRIO_LOWEST (&event_queue_lowest) -#endif +/** + * @brief Pointer to the event queue handling highest priority events + */ +#define EVENT_PRIO_HIGHEST (&event_thread_queues[EVENT_QUEUE_PRIO_HIGHEST]) +/** + * @brief Pointer to the event queue handling medium priority events + */ +#define EVENT_PRIO_MEDIUM (&event_thread_queues[EVENT_QUEUE_PRIO_MEDIUM]) +/** + * @brief Pointer to the event queue handling lowest priority events + */ +#define EVENT_PRIO_LOWEST (&event_thread_queues[EVENT_QUEUE_PRIO_LOWEST]) #ifdef __cplusplus }