mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
sys/event: Allow shared thread for multiple queues
Allow using `event_loop_multi()` to handle event queues of multiple priorities in an single thread. In the extreme case, all three event queues are handled by a single thread (thus saving two stacks). This comes for the price of increased worst case latency, as already running event handlers will no longer be preempted by higher priority events. With this, all three event queue priorities are always provided. Using modules, the old behavior of one thread per event queue can be restored for better worst case latency at the expense of additional thread size.
This commit is contained in:
parent
955b1f5be8
commit
c6211cc6c2
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user