Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,18 @@ ifneq (,$(filter sock_dns,$(USEMODULE)))
USEMODULE += sock_util
endif

ifneq (,$(filter event_%,$(USEMODULE)))
USEMODULE += event
endif

ifneq (,$(filter event_timeout,$(USEMODULE)))
USEMODULE += xtimer
endif

ifneq (,$(filter event,$(USEMODULE)))
USEMODULE += core_thread_flags
endif

ifneq (,$(filter spiffs,$(USEMODULE)))
USEPKG += spiffs
USEMODULE += vfs
Expand Down
1 change: 1 addition & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ PSEUDOMODULES += cbor_semantic_tagging
PSEUDOMODULES += conn_can_isotp_multi
PSEUDOMODULES += core_%
PSEUDOMODULES += emb6_router
PSEUDOMODULES += event_%
PSEUDOMODULES += gnrc_ipv6_default
PSEUDOMODULES += gnrc_ipv6_router
PSEUDOMODULES += gnrc_ipv6_router_default
Expand Down
5 changes: 5 additions & 0 deletions sys/event/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SRC := event.c

SUBMODULES = 1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is managed pretty transparent here. For core I was understanding of that, but in sys the sub-folders for sub-modules is already established (also you don't need pseudo-modules then ;-)).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand transparent.
Using submodules here saves two folders and two makefiles with 2 lines each.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But makes it harder for newcomers to understand what to include ... (that's what I meant with transparent)


include $(RIOTBASE)/Makefile.base
22 changes: 22 additions & 0 deletions sys/event/callback.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/

#include "event/callback.h"

void _event_callback_handler(event_t *event)
{
event_callback_t *event_callback = (event_callback_t *) event;
event_callback->callback(event_callback->arg);
}

void event_callback_init(event_callback_t *event_callback, void (callback)(void *), void *arg)
{
event_callback->super.handler = _event_callback_handler;
event_callback->callback = callback;
event_callback->arg = arg;
}
79 changes: 79 additions & 0 deletions sys/event/event.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/

#include <assert.h>

#include <string.h>

#include "event.h"
#include "clist.h"
#include "thread.h"

void event_queue_init(event_queue_t *queue)
{
assert(queue);
memset(queue, '\0', sizeof(*queue));
queue->waiter = (thread_t *)sched_active_thread;
}

void event_post(event_queue_t *queue, event_t *event)
{
assert(!event->list_node.next);
assert(queue->waiter);

unsigned state = irq_disable();
clist_rpush(&queue->event_list, &event->list_node);
irq_restore(state);

thread_flags_set(queue->waiter, THREAD_FLAG_EVENT);
}

void event_cancel(event_queue_t *queue, event_t *event)
{
assert(queue);
assert(event);

unsigned state = irq_disable();
clist_remove(&queue->event_list, &event->list_node);
event->list_node.next = NULL;
irq_restore(state);
}

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;
}
return result;
}

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);
result->list_node.next = NULL;
return result;
}

void event_loop(event_queue_t *queue)
{
event_t *event;

while ((event = event_wait(queue))) {
event->handler(event);
}
}
28 changes: 28 additions & 0 deletions sys/event/timeout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/

#include "event/timeout.h"

static void _event_timeout_callback(void *arg)
{
event_timeout_t *event_timeout = (event_timeout_t *)arg;
event_post(event_timeout->queue, event_timeout->event);
}

void event_timeout_init(event_timeout_t *event_timeout, event_queue_t *queue, event_t *event)
{
event_timeout->timer.callback = _event_timeout_callback;
event_timeout->timer.arg = event_timeout;
event_timeout->queue = queue;
event_timeout->event = event;
}

void event_timeout_set(event_timeout_t *event_timeout, uint32_t timeout)
{
xtimer_set(&event_timeout->timer, timeout);
}
218 changes: 218 additions & 0 deletions sys/include/event.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/

/**
* @defgroup sys_event Event Queue
* @ingroup sys
* @brief Provides an Event loop
*
* This module offers an event queue framework like libevent or libuev.
*
* 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.
*
* 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
* embedded into existing structures (see examples).
*
* Compared to msg or mbox, this some fundamental differences:
*
* 1. events are "sender allocated". Unlike msg_send(), event_post() never
* blocks or fails.
* 2. events contain everything necessary to handle them, thus a thread
* processing the events of an event queue doesn't need to be changed in
* order to support new event types.
* 3. events can be safely used (and actually perform best) when used within
* one thread, e.g., in order to create a state-machine like process flow.
* This is not (easily) possible using msg queues, as they might fill up.
* 4. an event can only be queued in one event queue at the same time.
* Notifying many queues using only one event object is not possible with
* this imlementation.
*
* At the core, event_wait() uses thread flags to implement waiting for events
* to be queued. Thus event queues can be used safely and efficiently in combination
* with thread flags and msg queues.
*
* Examples:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* // simple event handler
* static void handler(event_t *event)
* {
* printf("triggered 0x%08x\n", (unsigned)event);
* }
*
* static event_t event = { .handler = handler };
* static event_queue_t queue;
*
* int main(void)
* {
* event_queue_init(&queue);
* event_loop(&queue);
* }
*
* [...] event_post(&queue, &event);
*
* // example for event extended event struct
* typedef struct {
* event_t super;
* const char *text;
* } custom_event_t;
*
* static void custom_handler(event_t *event)
* {
* custom_event_t *custom_event = (custom_event_t *)event;
* printf("triggered custom event with text: \"%s\"\n", custom_event->text);
* }
*
* static custom_event_t custom_event = { .super.callback = custom_handler, .text = "CUSTOM EVENT" };
*
* [...] event_post(&queue, &custom_event)
* ~~~~~~~~~~~~~~~~~~~~~~~~
*
* @{
*
* @file
* @brief Event API
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/

#ifndef EVENT_H
#define EVENT_H

#include <stdint.h>

#include "irq.h"
#include "thread_flags.h"
#include "clist.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifndef THREAD_FLAG_EVENT
/**
* @brief Thread flag use to notify available events in an event queue
*/
#define THREAD_FLAG_EVENT (0x1)
#endif

/**
* @brief event_queue_t static initializer
*/
#define EVENT_QUEUE_INIT { .waiter = (thread_t *)sched_active_thread }

/**
* @brief event structure forward declaration
*/
typedef struct event event_t;

/**
* @brief event handler type definition
*/
typedef void (*event_handler_t)(event_t *);

/**
* @brief event structure
*/
struct event {
clist_node_t list_node; /**< event queue list entry */
event_handler_t handler; /**< pointer to event handler function */
};

/**
* @brief event queue structure
*/
typedef struct {
clist_node_t event_list; /**< list of queued events */
thread_t *waiter; /**< thread ownning event queue */
} event_queue_t;

/**
* @brief Initialize an event queue
*
* This will set the calling thread as owner of @p queue.
*
* @param[out] queue event queue object to initialize
*/
void event_queue_init(event_queue_t *queue);

/**
* @brief Queue an event
*
* @param[in] queue event queue to queue event in
* @param[in] event event to queue in event queue
*/
void event_post(event_queue_t *queue, event_t *event);

/**
* @brief Cancel a queued event
*
* This will remove a queued event from an event queue.
*
* @note Due to the underlying list implementation, this will run in O(n).
*
* @param[in] queue event queue to remove event from
* @param[in] event event to remove from queue
*/
void event_cancel(event_queue_t *queue, event_t *event);

/**
* @brief Get next event from event queue, non-blocking
*
* In order to handle an event retrieved using this function,
* call event->handler(event).
*
* @param[in] queue event queue to get event from
*
* @returns pointer to next event
* @returns NULL if no event available
*/
event_t *event_get(event_queue_t *queue);

/**
* @brief Get next event from event queue, blocking
*
* This function will block until an event becomes available.
*
* In order to handle an event retrieved using this function,
* call event->handler(event).
*
* @param[in] queue event queue to get event from
*
* @returns pointer to next event
*/
event_t *event_wait(event_queue_t *queue);

/**
* @brief Simple event loop
*
* This function will forever sit in a loop, waiting for events to be queued
* and executing their handlers.
*
* It is pretty much defined as:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* while ((event = event_wait(queue))) {
* event->handler(event);
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @param[in] queue event queue to process
*/
void event_loop(event_queue_t *queue);

#ifdef __cplusplus
}
#endif
#endif /* EVENT_H */
/** @} */
Loading