/* * Copyright (C) 2019 Kaspar Schleiser * 2018 Freie Universität Berlin * 2020 Inria * * 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 Thread implementation * * @author Hauke Petersen * @author Kaspar Schleiser * * @} */ #include "architecture.h" #include "thread.h" #include "event.h" #include "event/thread.h" struct event_queue_and_size { event_queue_t *q; size_t q_numof; }; static void *_handler_thread(void *tagged_ptr) { 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_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 * initialized at that point. * * They will be claimed within the handler thread. */ event_queues_init_detached(queues, queues_numof); 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 # ifdef ISR_STACKSIZE # define EVENT_THREAD_STACKSIZE_DEFAULT ISR_STACKSIZE # else # define EVENT_THREAD_STACKSIZE_DEFAULT THREAD_STACKSIZE_SMALL # endif #endif #ifndef EVENT_THREAD_HIGHEST_STACKSIZE #define EVENT_THREAD_HIGHEST_STACKSIZE EVENT_THREAD_STACKSIZE_DEFAULT #endif #ifndef EVENT_THREAD_HIGHEST_PRIO #define EVENT_THREAD_HIGHEST_PRIO (0) #endif #ifndef EVENT_THREAD_MEDIUM_STACKSIZE #define EVENT_THREAD_MEDIUM_STACKSIZE EVENT_THREAD_STACKSIZE_DEFAULT #endif #ifndef EVENT_THREAD_MEDIUM_PRIO #define EVENT_THREAD_MEDIUM_PRIO (THREAD_PRIORITY_MAIN - 1) #endif #ifndef EVENT_THREAD_LOWEST_STACKSIZE #define EVENT_THREAD_LOWEST_STACKSIZE EVENT_THREAD_STACKSIZE_DEFAULT #endif #ifndef EVENT_THREAD_LOWEST_PRIO #define EVENT_THREAD_LOWEST_PRIO (THREAD_PRIORITY_IDLE - 1) #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]; event_queue_t event_thread_queues[EVENT_QUEUE_PRIO_NUMOF]; void auto_init_event_thread(void) { 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); }