1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-26 06:53:52 +01:00

Merge pull request #9181 from haukepetersen/opt_event_independencefromthread

sys/event: make event queue independent from thread
This commit is contained in:
Kaspar Schleiser 2019-02-16 13:50:14 +01:00 committed by GitHub
commit ac775b8ae5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 17 deletions

View File

@ -1,11 +1,25 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <assert.h>
#include <string.h>
@ -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;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#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

View File

@ -1,5 +1,7 @@
include ../Makefile.tests_common
BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-uno
FORCE_ASSERTS = 1
USEMODULE += event_callback
USEMODULE += event_timeout

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
* 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
@ -14,6 +15,7 @@
* @brief event test application
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
@ -25,22 +27,30 @@
#include "event/timeout.h"
#include "event/callback.h"
static unsigned order;
#define STACKSIZE THREAD_STACKSIZE_DEFAULT
#define PRIO (THREAD_PRIORITY_MAIN - 1)
static char stack[STACKSIZE];
static unsigned order = 0;
static uint32_t before;
static void callback(event_t *arg);
static void custom_callback(event_t *event);
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 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 void callback(event_t *arg)
{
order++;
assert(order == 1);
assert(order == 3);
assert(arg == &event);
printf("triggered 0x%08x\n", (unsigned)arg);
}
@ -57,7 +67,7 @@ static event_callback_t noevent_callback = EVENT_CALLBACK_INIT(forbidden_callbac
static void custom_callback(event_t *event)
{
order++;
assert(order == 2);
assert(order == 4);
assert(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);
@ -66,7 +76,7 @@ static void custom_callback(event_t *event)
static void timed_callback(void *arg)
{
order++;
assert(order == 3);
assert(order == 5);
assert(arg == event_callback.arg);
uint32_t now = xtimer_now_usec();
assert((now - before >= 100000LU));
@ -85,11 +95,54 @@ static void forbidden_callback(void *arg)
}
}
static void delayed_callback1(event_t *arg)
{
order++;
assert(order == 1);
assert(arg == &delayed_event1);
printf("triggered delayed event %p\n", (void *)arg);
}
static void delayed_callback2(event_t *arg)
{
order++;
assert(order == 2);
assert(arg == &delayed_event2);
printf("triggered delayed event %p\n", (void *)arg);
}
static void *claiming_thread(void *arg)
{
event_queue_t *dq = (event_queue_t *)arg;
printf("claiming event queue %p\n", (void *)dq);
event_queue_claim(dq);
printf("launching event queue %p\n", (void *)dq);
event_loop(dq);
return NULL;
}
int main(void)
{
puts("[START] event test application.\n");
event_queue_t queue = { .waiter = (thread_t *)sched_active_thread };
/* test creation of delayed claiming of a detached event queue */
event_queue_t dq;
printf("initializing detached event queue %p\n", (void *)&dq);
event_queue_init_detached(&dq);
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("running thread that will claim event queue %p\n", (void *)&dq);
thread_create(stack, sizeof(stack), PRIO, 0, claiming_thread, &dq, "ct");
/* test posting different kind of events in order to a statically
* initialized queue */
event_queue_t queue = EVENT_QUEUE_INIT;
printf("posting 0x%08x\n", (unsigned)&event);
event_post(&queue, &event);