Merge branch 'feature/predictable_event_dispatch_via_duplication' into 'master'

Predictable event handler dispatch

See merge request idf/esp-idf!3932
This commit is contained in:
Angus Gratton 2019-02-05 08:47:03 +08:00
commit 4e770aec61
6 changed files with 608 additions and 511 deletions

View file

@ -30,12 +30,10 @@
/* ---------------------------- Definitions --------------------------------- */
#ifdef CONFIG_EVENT_LOOP_PROFILING
// loop@<address,name> rx:<total_recieved> dr:<total_dropped> inv:<total_number_of_invocations> run:<total_runtime>
#define LOOP_DUMP_FORMAT "loop@%p,%s rx:%u dr:%u inv:%u run:%lld us\n"
// event@<base:id> proc:<total_processed> run:<total_runtime>
#define EVENT_DUMP_FORMAT "\tevent@%s:%d proc:%u run:%lld us\n"
// handler@<address> inv:<total_invoked> run:<total_runtime>
#define HANDLER_DUMP_FORMAT "\t\thandler@%p inv:%u run:%lld us\n"
// LOOP @<address, name> rx:<recieved events no.> dr:<dropped events no.>
#define LOOP_DUMP_FORMAT "LOOP @%p,%s rx:%u dr:%u\n"
// handler @<address> ev:<base, id> inv:<times invoked> time:<runtime>
#define HANDLER_DUMP_FORMAT " HANDLER @%p ev:%s,%s inv:%u time:%lld us\n"
#define PRINT_DUMP_INFO(dst, sz, ...) do { \
int cb = snprintf(dst, sz, __VA_ARGS__); \
@ -63,33 +61,33 @@ static portMUX_TYPE s_event_loops_spinlock = portMUX_INITIALIZER_UNLOCKED;
static int esp_event_dump_prepare()
{
esp_event_loop_instance_t* loop_it;
esp_event_base_instance_t* base_it;
esp_event_id_instance_t* id_it;
esp_event_loop_node_t *loop_node_it;
esp_event_base_node_t* base_node_it;
esp_event_id_node_t* id_node_it;
esp_event_handler_instance_t* handler_it;
// Count the number of items to be printed. This is needed to compute how much memory to reserve.
int loops = 0, events = 0, handlers = 0;
int loops = 0, handlers = 0;
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_FOREACH(loop_it, &s_event_loops, loop_entry) {
SLIST_FOREACH(handler_it, &(loop_it->loop_handlers), handler_entry) {
handlers++;
}
SLIST_FOREACH(base_it, &(loop_it->event_bases), event_base_entry) {
SLIST_FOREACH(handler_it, &(base_it->base_handlers), handler_entry) {
SLIST_FOREACH(loop_it, &s_event_loops, next) {
SLIST_FOREACH(loop_node_it, &(loop_it->loop_nodes), next) {
SLIST_FOREACH(handler_it, &(loop_node_it->handlers), next) {
handlers++;
}
// Print event-level handlers
SLIST_FOREACH(id_it, &(base_it->event_ids), event_id_entry) {
SLIST_FOREACH(handler_it, &(id_it->handlers), handler_entry) {
SLIST_FOREACH(base_node_it, &(loop_node_it->base_nodes), next) {
SLIST_FOREACH(handler_it, &(base_node_it->handlers), next) {
handlers++;
}
events++;
SLIST_FOREACH(id_node_it, &(base_node_it->id_nodes), next) {
SLIST_FOREACH(handler_it, &(id_node_it->handlers), next) {
handlers++;
}
}
}
events++;
}
events++;
loops++;
}
@ -97,9 +95,8 @@ static int esp_event_dump_prepare()
// Reserve slightly more memory than computed
int allowance = 3;
int size = (((loops + allowance) * (sizeof(LOOP_DUMP_FORMAT) + 10 + 20 + 3 * 11 + 20 )) +
((events + allowance) * (sizeof(EVENT_DUMP_FORMAT) + 10 + 20 + 11 + 20)) +
((handlers + allowance) * (sizeof(HANDLER_DUMP_FORMAT) + 10 + 11 + 20)));
int size = (((loops + allowance) * (sizeof(LOOP_DUMP_FORMAT) + 10 + 20 + 2 * 11)) +
((handlers + allowance) * (sizeof(HANDLER_DUMP_FORMAT) + 10 + 2 * 20 + 11 + 20)));
return size;
}
@ -123,167 +120,257 @@ static void esp_event_loop_run_task(void* args)
vTaskSuspend(NULL);
}
// Functions that operate on handler instance
static esp_event_handler_instance_t* handler_instance_create(esp_event_handler_t event_handler, void* event_handler_arg)
static void handler_execute(esp_event_loop_instance_t* loop, esp_event_handler_instance_t *handler, esp_event_post_instance_t post)
{
ESP_LOGD(TAG, "running post %s:%d with handler %p on loop %p", post.base, post.id, handler->handler, loop);
#ifdef CONFIG_EVENT_LOOP_PROFILING
int64_t start, diff;
start = esp_timer_get_time();
#endif
// Execute the handler
(*(handler->handler))(handler->arg, post.base, post.id, post.data);
#ifdef CONFIG_EVENT_LOOP_PROFILING
diff = esp_timer_get_time() - start;
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
handler->invoked++;
handler->time += diff;
xSemaphoreGive(loop->profiling_mutex);
#endif
}
static esp_err_t handler_instances_add(esp_event_handler_instances_t* handlers, esp_event_handler_t handler, void* handler_arg)
{
esp_event_handler_instance_t* handler_instance = calloc(1, sizeof(*handler_instance));
if (handler_instance != NULL) {
handler_instance->handler = event_handler;
handler_instance->arg = event_handler_arg;
if (!handler_instance) {
return ESP_ERR_NO_MEM;
}
return handler_instance;
handler_instance->handler = handler;
handler_instance->arg = handler_arg;
if(SLIST_EMPTY(handlers)) {
SLIST_INSERT_HEAD(handlers, handler_instance, next);
}
else {
esp_event_handler_instance_t *it = NULL, *last = NULL;
SLIST_FOREACH(it, handlers, next) {
if (handler == it->handler) {
it->arg = handler_arg;
ESP_LOGW(TAG, "handler already registered, overwriting");
return ESP_OK;
}
last = it;
}
SLIST_INSERT_AFTER(last, handler_instance, next);
}
return ESP_OK;
}
static void handler_instance_delete(esp_event_handler_instance_t* handler_instance)
static esp_err_t base_node_add_handler(esp_event_base_node_t* base_node, int32_t id, esp_event_handler_t handler, void* handler_arg)
{
free(handler_instance);
if (id == ESP_EVENT_ANY_ID) {
return handler_instances_add(&(base_node->handlers), handler, handler_arg);
}
else {
esp_err_t err = ESP_OK;
esp_event_id_node_t *it = NULL, *id_node = NULL, *last_id_node = NULL;
SLIST_FOREACH(it, &(base_node->id_nodes), next) {
if (it->id == id) {
id_node = it;
}
last_id_node = it;
}
if (!last_id_node || !id_node) {
id_node = (esp_event_id_node_t*) calloc(1, sizeof(*id_node));
if (!id_node) {
ESP_LOGI(TAG, "alloc for new id node failed");
return ESP_ERR_NO_MEM;
}
id_node->id = id;
SLIST_INIT(&(id_node->handlers));
err = handler_instances_add(&(id_node->handlers), handler, handler_arg);
if (err == ESP_OK) {
if (!last_id_node) {
SLIST_INSERT_HEAD(&(base_node->id_nodes), id_node, next);
}
else {
SLIST_INSERT_AFTER(last_id_node, id_node, next);
}
}
return err;
}
else {
return handler_instances_add(&(id_node->handlers), handler, handler_arg);
}
}
}
// Functions that operate on handler instance list
static esp_event_handler_instance_t* handler_instances_find(esp_event_handler_instances_t* handlers, esp_event_handler_t handler)
static esp_err_t loop_node_add_handler(esp_event_loop_node_t* loop_node, esp_event_base_t base, int32_t id, esp_event_handler_t handler, void* handler_arg)
{
esp_event_handler_instance_t* it;
if (base == esp_event_any_base && id == ESP_EVENT_ANY_ID) {
return handler_instances_add(&(loop_node->handlers), handler, handler_arg);
}
else {
esp_err_t err = ESP_OK;
esp_event_base_node_t *it = NULL, *base_node = NULL, *last_base_node = NULL;
SLIST_FOREACH(it, handlers, handler_entry) {
SLIST_FOREACH(it, &(loop_node->base_nodes), next) {
if (it->base == base) {
base_node = it;
}
last_base_node = it;
}
if (!last_base_node ||
!base_node ||
(base_node && !SLIST_EMPTY(&(base_node->id_nodes)) && id == ESP_EVENT_ANY_ID) ||
(last_base_node && last_base_node->base != base && !SLIST_EMPTY(&(last_base_node->id_nodes)) && id == ESP_EVENT_ANY_ID)) {
base_node = (esp_event_base_node_t*) calloc(1, sizeof(*base_node));
if (!base_node) {
ESP_LOGE(TAG, "alloc mem for new base node failed");
return ESP_ERR_NO_MEM;
}
base_node->base = base;
SLIST_INIT(&(base_node->handlers));
SLIST_INIT(&(base_node->id_nodes));
err = base_node_add_handler(base_node, id, handler, handler_arg);
if (err == ESP_OK) {
if (!last_base_node) {
SLIST_INSERT_HEAD(&(loop_node->base_nodes), base_node, next);
}
else {
SLIST_INSERT_AFTER(last_base_node, base_node, next);
}
}
return err;
} else {
return base_node_add_handler(base_node, id, handler, handler_arg);
}
}
}
static esp_err_t handler_instances_remove(esp_event_handler_instances_t* handlers, esp_event_handler_t handler)
{
esp_event_handler_instance_t *it, *temp;
SLIST_FOREACH_SAFE(it, handlers, next, temp) {
if (it->handler == handler) {
break;
SLIST_REMOVE(handlers, it, esp_event_handler_instance, next);
free(it);
return ESP_OK;
}
}
return it;
return ESP_ERR_NOT_FOUND;
}
static void handler_instances_add(esp_event_handler_instances_t* handlers, esp_event_handler_instance_t* handler_instance)
static esp_err_t base_node_remove_handler(esp_event_base_node_t* base_node, int32_t id, esp_event_handler_t handler)
{
SLIST_INSERT_HEAD(handlers, handler_instance, handler_entry);
if (id == ESP_EVENT_ANY_ID) {
return handler_instances_remove(&(base_node->handlers), handler);
}
else {
esp_event_id_node_t *it, *temp;
SLIST_FOREACH_SAFE(it, &(base_node->id_nodes), next, temp) {
if (it->id == id) {
esp_err_t res = handler_instances_remove(&(it->handlers), handler);
if (res == ESP_OK) {
if (SLIST_EMPTY(&(it->handlers))) {
SLIST_REMOVE(&(base_node->id_nodes), it, esp_event_id_node, next);
free(it);
return ESP_OK;
}
}
}
}
}
return ESP_ERR_NOT_FOUND;
}
static void handler_instances_remove(esp_event_handler_instances_t* handlers, esp_event_handler_instance_t* handler_instance)
static esp_err_t loop_node_remove_handler(esp_event_loop_node_t* loop_node, esp_event_base_t base, int32_t id, esp_event_handler_t handler)
{
SLIST_REMOVE(handlers, handler_instance, esp_event_handler_instance, handler_entry);
handler_instance_delete(handler_instance);
if (base == esp_event_any_base && id == ESP_EVENT_ANY_ID) {
return handler_instances_remove(&(loop_node->handlers), handler);
}
else {
esp_event_base_node_t *it, *temp;
SLIST_FOREACH_SAFE(it, &(loop_node->base_nodes), next, temp) {
if (it->base == base) {
esp_err_t res = base_node_remove_handler(it, id, handler);
if (res == ESP_OK) {
if (SLIST_EMPTY(&(it->handlers)) && SLIST_EMPTY(&(it->id_nodes))) {
SLIST_REMOVE(&(loop_node->base_nodes), it, esp_event_base_node, next);
free(it);
return ESP_OK;
}
}
}
}
}
return ESP_ERR_NOT_FOUND;
}
static void handler_instances_remove_all(esp_event_handler_instances_t* handlers)
{
esp_event_handler_instance_t* it;
esp_event_handler_instance_t* temp;
SLIST_FOREACH_SAFE(it, handlers, handler_entry, temp) {
handler_instances_remove(handlers, it);
esp_event_handler_instance_t *it, *temp;
SLIST_FOREACH_SAFE(it, handlers, next, temp) {
SLIST_REMOVE(handlers, it, esp_event_handler_instance, next);
free(it);
}
}
// Functions that operate on event id instance
static void* event_id_instance_create(int32_t event_id)
static void base_node_remove_all_handler(esp_event_base_node_t* base_node)
{
esp_event_id_instance_t* event_id_instance = calloc(1, sizeof(*event_id_instance));
handler_instances_remove_all(&(base_node->handlers));
if (event_id_instance != NULL) {
event_id_instance->id = event_id;
SLIST_INIT(&(event_id_instance->handlers));
}
return event_id_instance;
}
static void event_id_instance_delete(esp_event_id_instance_t* event_id_instance)
{
handler_instances_remove_all(&(event_id_instance->handlers));
free(event_id_instance);
}
// Functions that operate on event id instance list
static void event_id_instances_remove(esp_event_id_instances_t* head, esp_event_id_instance_t* event_id_instance)
{
SLIST_REMOVE(head, event_id_instance, esp_event_id_instance, event_id_entry);
event_id_instance_delete(event_id_instance);
}
// Functions that operate on event base instance
static esp_event_base_instance_t* event_base_instance_create(esp_event_base_t event_base)
{
esp_event_base_instance_t* event_base_instance = calloc(1, sizeof(*event_base_instance));
if (event_base_instance != NULL) {
event_base_instance->base = event_base;
SLIST_INIT(&(event_base_instance->base_handlers));
SLIST_INIT(&(event_base_instance->event_ids));
}
return event_base_instance;
}
static void event_base_instance_delete(esp_event_base_instance_t* event_base_instance)
{
esp_event_id_instance_t* it;
esp_event_id_instance_t* temp;
handler_instances_remove_all(&(event_base_instance->base_handlers));
SLIST_FOREACH_SAFE(it, &(event_base_instance->event_ids), event_id_entry, temp) {
event_id_instances_remove(&(event_base_instance->event_ids), it);
}
free(event_base_instance);
}
static void event_base_instance_add_event_id_instance(esp_event_base_instance_t* event_base_instance, esp_event_id_instance_t* event_id_instance)
{
SLIST_INSERT_HEAD(&(event_base_instance->event_ids), event_id_instance, event_id_entry);
}
static esp_event_id_instance_t* event_base_instance_find_event_id_instance(esp_event_base_instance_t* event_base_instance, int32_t event_id)
{
esp_event_id_instance_t* it;
SLIST_FOREACH(it, &(event_base_instance->event_ids), event_id_entry) {
if (it->id == event_id) {
break;
}
}
return it;
}
// Functions that operate on event base instances list
static void event_base_instances_remove(esp_event_base_instances_t* head, esp_event_base_instance_t* event_base_instance)
{
SLIST_REMOVE(head, event_base_instance, esp_event_base_instance, event_base_entry);
event_base_instance_delete(event_base_instance);
}
// Functions that operate on loop instances
static void loop_add_event_base_instance(esp_event_loop_instance_t* loop, esp_event_base_instance_t* event_base_instance) {
SLIST_INSERT_HEAD(&(loop->event_bases), event_base_instance, event_base_entry);
}
static void loop_remove_all_event_base_instance(esp_event_loop_instance_t* loop)
{
esp_event_base_instance_t* it;
esp_event_base_instance_t* temp;
SLIST_FOREACH_SAFE(it, &(loop->event_bases), event_base_entry, temp) {
event_base_instances_remove(&(loop->event_bases), it);
esp_event_id_node_t *it, *temp;
SLIST_FOREACH_SAFE(it, &(base_node->id_nodes), next, temp) {
handler_instances_remove_all(&(it->handlers));
SLIST_REMOVE(&(base_node->id_nodes), it, esp_event_id_node, next);
free(it);
}
}
static esp_event_base_instance_t* loop_find_event_base_instance(esp_event_loop_instance_t* loop, esp_event_base_t event_base)
static void loop_node_remove_all_handler(esp_event_loop_node_t* loop_node)
{
esp_event_base_instance_t* it;
handler_instances_remove_all(&(loop_node->handlers));
SLIST_FOREACH(it, &(loop->event_bases), event_base_entry) {
if (it->base == event_base) {
break;
}
esp_event_base_node_t *it, *temp;
SLIST_FOREACH_SAFE(it, &(loop_node->base_nodes), next, temp) {
base_node_remove_all_handler(it);
SLIST_REMOVE(&(loop_node->base_nodes), it, esp_event_base_node, next);
free(it);
}
return it;
}
// Functions that operate on post instance
static esp_err_t post_instance_create(esp_event_base_t event_base, int32_t event_id, void* event_data, int32_t event_data_size, esp_event_post_instance_t* post)
{
void* event_data_copy = NULL;
@ -314,33 +401,6 @@ static void post_instance_delete(esp_event_post_instance_t* post)
free(post->data);
}
static esp_event_handler_instances_t* find_handlers_list(esp_event_loop_instance_t* loop, esp_event_base_t event_base,
int32_t event_id)
{
esp_event_handler_instances_t* handlers = NULL;
esp_event_base_instance_t* base = NULL;
esp_event_id_instance_t* event = NULL;
if (event_base == esp_event_any_base && event_id == ESP_EVENT_ANY_ID) {
handlers = &(loop->loop_handlers);
} else {
base = loop_find_event_base_instance(loop, event_base);
if (base != NULL) {
if (event_id == ESP_EVENT_ANY_ID) {
handlers = &(base->base_handlers);
} else {
event = event_base_instance_find_event_id_instance(base, event_id);
if (event != NULL) {
handlers = &(event->handlers);
}
}
}
}
return handlers;
}
/* ---------------------------- Public API --------------------------------- */
esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, esp_event_loop_handle_t* event_loop)
@ -376,8 +436,7 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
}
#endif
SLIST_INIT(&(loop->loop_handlers));
SLIST_INIT(&(loop->event_bases));
SLIST_INIT(&(loop->loop_nodes));
// Create the loop task if requested
if (event_loop_args->task_name != NULL) {
@ -403,7 +462,7 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
#ifdef CONFIG_EVENT_LOOP_PROFILING
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_INSERT_HEAD(&s_event_loops, loop, loop_entry);
SLIST_INSERT_HEAD(&s_event_loops, loop, next);
portEXIT_CRITICAL(&s_event_loops_spinlock);
#endif
@ -447,7 +506,6 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
esp_event_post_instance_t post;
TickType_t marker = xTaskGetTickCount();
TickType_t end = 0;
esp_event_handler_instance_t* temp;
#if( configUSE_16_BIT_TICKS == 1 )
int32_t remaining_ticks = ticks_to_run;
@ -456,76 +514,46 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
#endif
while(xQueueReceive(loop->queue, &post, ticks_to_run) == pdTRUE) {
esp_event_base_instance_t* base = NULL;
esp_event_id_instance_t* event = NULL;
// Reserve space for three possible matches: (1) the entry for handlers registered to all events in the loop, the
// (2) entry matching events with a specified base and (3) the entry matching both base and id.
#define LOOP_LEVEL_HANDLER 0
#define BASE_LEVEL_HANDLER 1
#define EVENT_LEVEL_HANDLER 2
esp_event_handler_instances_t* handlers_list[EVENT_LEVEL_HANDLER + 1] = {0};
// The event has already been unqueued, so ensure it gets executed.
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
loop->running_task = xTaskGetCurrentTaskHandle();
handlers_list[LOOP_LEVEL_HANDLER] = &(loop->loop_handlers);
base = loop_find_event_base_instance(loop, post.base);
if (base) {
event = event_base_instance_find_event_id_instance(base, post.id);
handlers_list[BASE_LEVEL_HANDLER] = &(base->base_handlers);
if (event) {
handlers_list[EVENT_LEVEL_HANDLER] = &(event->handlers);
}
}
loop->running_task = xTaskGetCurrentTaskHandle();
bool exec = false;
for (int i = LOOP_LEVEL_HANDLER; i <= EVENT_LEVEL_HANDLER; i++) {
if (handlers_list[i] != NULL) {
esp_event_handler_instance_t* it;
SLIST_FOREACH_SAFE(it, handlers_list[i], handler_entry, temp) {
ESP_LOGD(TAG, "running post %s:%d with handler %p on loop %p", post.base, post.id, it->handler, event_loop);
esp_event_handler_instance_t *handler;
esp_event_loop_node_t *loop_node;
esp_event_base_node_t *base_node;
esp_event_id_node_t *id_node;
#ifdef CONFIG_EVENT_LOOP_PROFILING
int64_t start, diff;
start = esp_timer_get_time();
#endif
// Execute the handler
(*(it->handler))(it->arg, post.base, post.id, post.data);
SLIST_FOREACH(loop_node, &(loop->loop_nodes), next) {
// Execute loop level handlers
SLIST_FOREACH(handler, &(loop_node->handlers), next) {
handler_execute(loop, handler, post);
exec |= true;
}
#ifdef CONFIG_EVENT_LOOP_PROFILING
diff = esp_timer_get_time() - start;
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
it->total_times_invoked++;
it->total_runtime += diff;
if (i == LOOP_LEVEL_HANDLER) {
loop->loop_handlers_invoked++;
loop->loop_handlers_runtime += diff;
} else if (i == BASE_LEVEL_HANDLER) {
base->base_handlers_invoked++;
base->base_handlers_runtime += diff;
} else {
event->handlers_invoked++;
event->handlers_runtime += diff;
SLIST_FOREACH(base_node, &(loop_node->base_nodes), next) {
if (base_node->base == post.base) {
// Execute base level handlers
SLIST_FOREACH(handler, &(base_node->handlers), next) {
handler_execute(loop, handler, post);
exec |= true;
}
loop->total_handlers_invoked++;
loop->total_handlers_runtime += diff;
xSemaphoreGive(loop->profiling_mutex);
#endif
SLIST_FOREACH(id_node, &(base_node->id_nodes), next) {
if(id_node->id == post.id) {
// Execute id level handlers
SLIST_FOREACH(handler, &(id_node->handlers), next) {
handler_execute(loop, handler, post);
exec |= true;
}
// Skip to next base node
break;
}
}
}
}
exec |= true;
}
post_instance_delete(&post);
@ -570,7 +598,7 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
#ifdef CONFIG_EVENT_LOOP_PROFILING
xSemaphoreTakeRecursive(loop->profiling_mutex, portMAX_DELAY);
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_REMOVE(&s_event_loops, loop, esp_event_loop_instance, loop_entry);
SLIST_REMOVE(&s_event_loops, loop, esp_event_loop_instance, next);
portEXIT_CRITICAL(&s_event_loops_spinlock);
#endif
@ -579,9 +607,13 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
vTaskDelete(loop->task);
}
// Remove all registered events in the loop
handler_instances_remove_all(&(loop->loop_handlers));
loop_remove_all_event_base_instance(loop);
// Remove all registered events and handlers in the loop
esp_event_loop_node_t *it, *temp;
SLIST_FOREACH_SAFE(it, &(loop->loop_nodes), next, temp) {
loop_node_remove_all_handler(it);
SLIST_REMOVE(&(loop->loop_nodes), it, esp_event_loop_node, next);
free(it);
}
// Drop existing posts on the queue
esp_event_post_instance_t post;
@ -618,89 +650,55 @@ esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop, es
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
esp_event_base_instance_t* base = NULL;
esp_event_id_instance_t* event = NULL;
esp_event_handler_instance_t* handler = NULL;
esp_event_handler_instances_t* handlers = NULL;
bool base_created = false;
bool event_created = false;
if (event_base == ESP_EVENT_ANY_BASE) {
event_base = esp_event_any_base;
}
esp_err_t err = ESP_OK;
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
if (event_base == esp_event_any_base && event_id == ESP_EVENT_ANY_ID) {
// Add to the loop-level handlers
handlers = &(loop->loop_handlers);
} else {
// If base instance does not exist, create one
if ((base = loop_find_event_base_instance(loop, event_base)) == NULL) {
base = event_base_instance_create(event_base);
if (base == NULL) {
xSemaphoreGiveRecursive(loop->mutex);
return ESP_ERR_NO_MEM;
}
base_created = true;
}
// Add to the event base instance level handlers
if (event_id == ESP_EVENT_ANY_ID) {
handlers = &(base->base_handlers);
} else {
if (base_created ||
(event = event_base_instance_find_event_id_instance(base, event_id)) == NULL) {
event = event_id_instance_create(event_id);
// If it does not exist, create one
if (event == NULL) {
if (base_created) {
event_base_instance_delete(base);
}
xSemaphoreGiveRecursive(loop->mutex);
return ESP_ERR_NO_MEM;
}
event_created = true;
}
// Add to the event id instance level handlers
handlers = &(event->handlers);
}
esp_event_loop_node_t *loop_node = NULL, *last_loop_node = NULL;
SLIST_FOREACH(loop_node, &(loop->loop_nodes), next) {
last_loop_node = loop_node;
}
// Add handler to the list
if (base_created || event_created ||
(handler = handler_instances_find(handlers, event_handler)) == NULL) {
handler = handler_instance_create(event_handler, event_handler_arg);
if (handler == NULL) {
if (event_created) {
event_id_instance_delete(event);
bool is_loop_level_handler = (event_base == esp_event_any_base) && (event_id == ESP_EVENT_ANY_ID);
if (!last_loop_node ||
(last_loop_node && !SLIST_EMPTY(&(last_loop_node->base_nodes)) && is_loop_level_handler)) {
loop_node = (esp_event_loop_node_t*) calloc(1, sizeof(*loop_node));
SLIST_INIT(&(loop_node->handlers));
SLIST_INIT(&(loop_node->base_nodes));
if (!loop_node) {
ESP_LOGE(TAG, "alloc for new loop node failed");
err = ESP_ERR_NO_MEM;
goto on_err;
}
err = loop_node_add_handler(loop_node, event_base, event_id, event_handler, event_handler_arg);
if (err == ESP_OK) {
if (!last_loop_node) {
SLIST_INSERT_HEAD(&(loop->loop_nodes), loop_node, next);
}
if (base_created) {
event_base_instance_delete(base);
else {
SLIST_INSERT_AFTER(last_loop_node, loop_node, next);
}
xSemaphoreGiveRecursive(loop->mutex);
return ESP_ERR_NO_MEM;
}
handler_instances_add(handlers, handler);
// If a new event base/ event id instance was created, add them to the appropriate list
if (event_created) {
event_base_instance_add_event_id_instance(base, event);
}
if (base_created) {
loop_add_event_base_instance(loop, base);
}
ESP_LOGD(TAG, "registered handler %p for event %s:%d", event_handler, event_base, event_id);
} else {
handler->arg = event_handler_arg;
ESP_LOGW(TAG, "handler %p for event %s:%d already registered, overwriting", event_handler, event_base, event_id);
}
else {
err = loop_node_add_handler(last_loop_node, event_base, event_id, event_handler, event_handler_arg);
}
on_err:
xSemaphoreGiveRecursive(loop->mutex);
return ESP_OK;
return err;
}
esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
int32_t event_id, esp_event_handler_t event_handler)
{
@ -718,17 +716,18 @@ esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop,
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
esp_event_handler_instance_t* handler = NULL;
esp_event_handler_instances_t* handlers = find_handlers_list(loop, event_base, event_id);
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
if (handlers != NULL &&
(handler = handler_instances_find(handlers, event_handler)) != NULL) {
handler_instances_remove(handlers, handler);
ESP_LOGD(TAG, "unregistered handler %p from event %s:%d", event_handler, event_base, event_id);
} else {
ESP_LOGW(TAG, "handler %p for event %s:%d not registered, ignoring", event_handler, event_base, event_id);
esp_event_loop_node_t *it, *temp;
SLIST_FOREACH_SAFE(it, &(loop->loop_nodes), next, temp) {
esp_err_t res = loop_node_remove_handler(it, event_base, event_id, event_handler);
if (res == ESP_OK && SLIST_EMPTY(&(it->base_nodes)) && SLIST_EMPTY(&(it->handlers))) {
SLIST_REMOVE(&(loop->loop_nodes), it, esp_event_loop_node, next);
free(it);
break;
}
}
xSemaphoreGiveRecursive(loop->mutex);
@ -804,14 +803,16 @@ esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop, esp_event_base_t
return ESP_OK;
}
esp_err_t esp_event_dump(FILE* file)
{
#ifdef CONFIG_EVENT_LOOP_PROFILING
assert(file);
esp_event_loop_instance_t* loop_it;
esp_event_base_instance_t* base_it;
esp_event_id_instance_t* id_it;
esp_event_loop_node_t *loop_node_it;
esp_event_base_node_t* base_node_it;
esp_event_id_node_t* id_node_it;
esp_event_handler_instance_t* handler_it;
// Allocate memory for printing
@ -819,46 +820,52 @@ esp_err_t esp_event_dump(FILE* file)
char* buf = calloc(sz, sizeof(char));
char* dst = buf;
char id_str_buf[20];
// Print info to buffer
portENTER_CRITICAL(&s_event_loops_spinlock);
SLIST_FOREACH(loop_it, &s_event_loops, loop_entry) {
PRINT_DUMP_INFO(dst, sz, LOOP_DUMP_FORMAT, loop_it, loop_it->name, loop_it->events_recieved,
loop_it->events_dropped, loop_it->total_handlers_invoked, loop_it->total_handlers_runtime);
// Print loop-level handler
PRINT_DUMP_INFO(dst, sz, esp_event_any_base, ESP_EVENT_ANY_ID, loop_it->loop_handlers_invoked,
loop_it->loop_handlers_runtime);
SLIST_FOREACH(handler_it, &(loop_it->loop_handlers), handler_entry) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler, handler_it->total_times_invoked,
handler_it->total_runtime);
}
SLIST_FOREACH(loop_it, &s_event_loops, next) {
PRINT_DUMP_INFO(dst, sz, LOOP_DUMP_FORMAT, loop_it, loop_it->task != NULL ? loop_it->name : "none" ,
loop_it->events_recieved, loop_it->events_dropped);
SLIST_FOREACH(base_it, &(loop_it->event_bases), event_base_entry) {
// Print base-level handler
PRINT_DUMP_INFO(dst, sz, EVENT_DUMP_FORMAT, base_it->base, ESP_EVENT_ANY_ID,
base_it->base_handlers_invoked, base_it->base_handlers_runtime);
SLIST_FOREACH(handler_it, &(base_it->base_handlers), handler_entry) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler,
handler_it->total_times_invoked, handler_it->total_runtime);
int sz_bak = sz;
SLIST_FOREACH(loop_node_it, &(loop_it->loop_nodes), next) {
SLIST_FOREACH(handler_it, &(loop_node_it->handlers), next) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler, "ESP_EVENT_ANY_BASE",
"ESP_EVENT_ANY_ID", handler_it->invoked, handler_it->time);
}
// Print event-level handlers
SLIST_FOREACH(id_it, &(base_it->event_ids), event_id_entry) {
PRINT_DUMP_INFO(dst, sz, EVENT_DUMP_FORMAT, base_it->base, id_it->id,
id_it->handlers_invoked, id_it->handlers_runtime);
SLIST_FOREACH(base_node_it, &(loop_node_it->base_nodes), next) {
SLIST_FOREACH(handler_it, &(base_node_it->handlers), next) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler, base_node_it->base ,
"ESP_EVENT_ANY_ID", handler_it->invoked, handler_it->time);
}
SLIST_FOREACH(handler_it, &(id_it->handlers), handler_entry) {
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler,
handler_it->total_times_invoked, handler_it->total_runtime);
SLIST_FOREACH(id_node_it, &(base_node_it->id_nodes), next) {
SLIST_FOREACH(handler_it, &(id_node_it->handlers), next) {
memset(id_str_buf, 0, sizeof(id_str_buf));
snprintf(id_str_buf, sizeof(id_str_buf), "%d", id_node_it->id);
PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler, base_node_it->base ,
id_str_buf, handler_it->invoked, handler_it->time);
}
}
}
}
// No handlers registered for this loop
if (sz == sz_bak) {
PRINT_DUMP_INFO(dst, sz, " NO HANDLERS REGISTERED\n");
}
}
portEXIT_CRITICAL(&s_event_loops_spinlock);
// Print the contents of the buffer to the file
fprintf(file, buf);
// Free the allocated buffer
free(buf);
#endif

View file

@ -22,17 +22,41 @@ bool esp_event_is_handler_registered(esp_event_loop_handle_t event_loop, esp_eve
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
bool result = false;
xSemaphoreTake(loop->mutex, portMAX_DELAY);
esp_event_base_instance_t* base_it;
SLIST_FOREACH(base_it, &(loop->event_bases), event_base_entry) {
esp_event_id_instance_t* event_it;
SLIST_FOREACH(event_it, &(base_it->event_ids), event_id_entry) {
esp_event_handler_instance_t* handler_it;
SLIST_FOREACH(handler_it, &(event_it->handlers), handler_entry) {
if (base_it->base == event_base && event_it->id == event_id && handler_it->handler == event_handler) {
result = true;
goto out;
esp_event_loop_node_t* loop_node;
esp_event_base_node_t* base_node;
esp_event_id_node_t* id_node;
esp_event_handler_instance_t* handler;
SLIST_FOREACH(loop_node, &(loop->loop_nodes), next) {
SLIST_FOREACH(handler, &(loop_node->handlers), next) {
if(event_base == ESP_EVENT_ANY_BASE && event_id == ESP_EVENT_ANY_ID && handler->handler == event_handler)
{
result = true;
goto out;
}
}
SLIST_FOREACH(base_node, &(loop_node->base_nodes), next) {
if (base_node->base == event_base) {
SLIST_FOREACH(handler, &(base_node->handlers), next) {
if(event_id == ESP_EVENT_ANY_ID && handler->handler == event_handler)
{
result = true;
goto out;
}
}
SLIST_FOREACH(id_node, &(base_node->id_nodes), next) {
if(id_node->id == event_id) {
SLIST_FOREACH(handler, &(id_node->handlers), next) {
if(handler->handler == event_handler)
{
result = true;
goto out;
}
}
}
}
}
}

View file

@ -33,11 +33,11 @@ extern "C" {
/// Configuration for creating event loops
typedef struct {
int32_t queue_size; /**< size of the event loop queue */
const char* task_name; /**< name of the event loop task; if NULL,
const char* task_name; /**< name of the event loop task; if NULL,
a dedicated task is not created for event loop*/
UBaseType_t task_priority; /**< priority of the event loop task, ignored if task name is NULL */
uint32_t task_stack_size; /**< stack size of the event loop task, ignored if task name is NULL */
BaseType_t task_core_id; /**< core to which the event loop task is pinned to,
BaseType_t task_core_id; /**< core to which the event loop task is pinned to,
ignored if task name is NULL */
} esp_event_loop_args_t;
@ -47,7 +47,7 @@ typedef struct {
* @param[in] event_loop_args configuration structure for the event loop to create
* @param[out] event_loop handle to the created event loop
*
* @return
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list
* - ESP_FAIL: Failed to create task loop
@ -60,7 +60,7 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
*
* @param[in] event_loop event loop to delete
*
* @return
* @return
* - ESP_OK: Success
* - Others: Fail
*/
@ -68,8 +68,8 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop);
/**
* @brief Create default event loop
*
* @return
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list
* - ESP_FAIL: Failed to create task loop
@ -79,8 +79,8 @@ esp_err_t esp_event_loop_create_default();
/**
* @brief Delete the default event loop
*
* @return
*
* @return
* - ESP_OK: Success
* - Others: Fail
*/
@ -89,18 +89,18 @@ esp_err_t esp_event_loop_delete_default();
/**
* @brief Dispatch events posted to an event loop.
*
* This function is used to dispatch events posted to a loop with no dedicated task, i.e task name was set to NULL
* in event_loop_args argument during loop creation. This function includes an argument to limit the amount of time
* it runs, returning control to the caller when that time expires (or some time afterwards). There is no guarantee
* that a call to this function will exit at exactly the time of expiry. There is also no guarantee that events have
* This function is used to dispatch events posted to a loop with no dedicated task, i.e task name was set to NULL
* in event_loop_args argument during loop creation. This function includes an argument to limit the amount of time
* it runs, returning control to the caller when that time expires (or some time afterwards). There is no guarantee
* that a call to this function will exit at exactly the time of expiry. There is also no guarantee that events have
* been dispatched during the call, as the function might have spent all of the alloted time waiting on the event queue.
* Once an event has been unqueued, however, it is guaranteed to be dispatched. This guarantee contributes to not being
* able to exit exactly at time of expiry as (1) blocking on internal mutexes is necessary for dispatching the unqueued
* event, and (2) during dispatch of the unqueued event there is no way to control the time occupied by handler code
* Once an event has been unqueued, however, it is guaranteed to be dispatched. This guarantee contributes to not being
* able to exit exactly at time of expiry as (1) blocking on internal mutexes is necessary for dispatching the unqueued
* event, and (2) during dispatch of the unqueued event there is no way to control the time occupied by handler code
* execution. The guaranteed time of exit is therefore the alloted time + amount of time required to dispatch
* the last unqueued event.
*
* In cases where waiting on the queue times out, ESP_OK is returned and not ESP_ERR_TIMEOUT, since it is
* In cases where waiting on the queue times out, ESP_OK is returned and not ESP_ERR_TIMEOUT, since it is
* normal behavior.
*
* @param[in] event_loop event loop to dispatch posted events from
@ -108,7 +108,7 @@ esp_err_t esp_event_loop_delete_default();
*
* @note encountering an unknown event that has been posted to the loop will only generate a warning, not an error.
*
* @return
* @return
* - ESP_OK: Success
* - Others: Fail
*/
@ -124,8 +124,8 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
* - all events of a certain base: specify exact event_base and use ESP_EVENT_ANY_ID as the event_id
* - all events known by the loop: use ESP_EVENT_ANY_BASE for event_base and ESP_EVENT_ANY_ID as the event_id
*
* Registering multiple handlers to events is possible. Registering a single handler to multiple events is
* also possible. However, registering the same handler to the same event multiple times would cause the
* Registering multiple handlers to events is possible. Registering a single handler to multiple events is
* also possible. However, registering the same handler to the same event multiple times would cause the
* previous registrations to be overwritten.
*
* @param[in] event_base the base id of the event to register the handler for
@ -133,24 +133,24 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
* @param[in] event_handler the handler function which gets called when the event is dispatched
* @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called
*
* @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should
* @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should
* ensure that event_handler_arg still points to a valid location by the time the handler gets called
*
* @return
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for the handler
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_handler_register(esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler,
esp_err_t esp_event_handler_register(esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler,
void* event_handler_arg);
/**
* @brief Register an event handler to a specific loop.
*
* This function behaves in the same manner as esp_event_handler_register, except the additional
* This function behaves in the same manner as esp_event_handler_register, except the additional
* specification of the event loop to register the handler to.
*
* @param[in] event_loop the event loop to register this handler function to
@ -159,23 +159,26 @@ esp_err_t esp_event_handler_register(esp_event_base_t event_base,
* @param[in] event_handler the handler function which gets called when the event is dispatched
* @param[in] event_handler_arg data, aside from event data, that is passed to the handler when it is called
*
* @return
* @note the event loop library does not maintain a copy of event_handler_arg, therefore the user should
* ensure that event_handler_arg still points to a valid location by the time the handler gets called
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for the handler
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler,
esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler,
void* event_handler_arg);
/**
* @brief Unregister a handler with the system event loop.
*
* This function can be used to unregister a handler so that it no longer gets called during dispatch.
* Handlers can be unregistered for either: (1) specific events, (2) all events of a certain event base,
* Handlers can be unregistered for either: (1) specific events, (2) all events of a certain event base,
* or (3) all events known by the system event loop
*
* - specific events: specify exact event_base and event_id
@ -189,7 +192,7 @@ esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop,
* @param[in] event_handler the handler to unregister
*
* @return ESP_OK success
* @return ESP_ERR_INVALIG_ARG invalid combination of event base and event id
* @return ESP_ERR_INVALID_ARG invalid combination of event base and event id
* @return others fail
*/
esp_err_t esp_event_handler_unregister(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler);
@ -197,7 +200,7 @@ esp_err_t esp_event_handler_unregister(esp_event_base_t event_base, int32_t even
/**
* @brief Unregister a handler with the system event loop.
*
* This function behaves in the same manner as esp_event_handler_unregister, except the additional specification of
* This function behaves in the same manner as esp_event_handler_unregister, except the additional specification of
* the event loop to unregister the handler with.
*
* @param[in] event_loop the event loop with which to unregister this handler function
@ -205,21 +208,21 @@ esp_err_t esp_event_handler_unregister(esp_event_base_t event_base, int32_t even
* @param[in] event_id the id of the event with which to unregister the handler
* @param[in] event_handler the handler to unregister
*
* @return
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
esp_event_handler_t event_handler);
/**
* @brief Posts an event to the system default event loop. The event loop library keeps a copy of event_data and manages
* the copy's lifetime automatically (allocation + deletion); this ensures that the data the
* @brief Posts an event to the system default event loop. The event loop library keeps a copy of event_data and manages
* the copy's lifetime automatically (allocation + deletion); this ensures that the data the
* handler recieves is always valid.
*
*
* @param[in] event_base the event base that identifies the event
* @param[in] event_id the the event id that identifies the event
* @param[in] event_data the data, specific to the event occurence, that gets passed to the handler
@ -228,21 +231,21 @@ esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop,
*
* @note posting events from an ISR is not supported
*
* @return
* @return
* - ESP_OK: Success
* - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_post(esp_event_base_t event_base,
int32_t event_id,
void* event_data,
size_t event_data_size,
esp_err_t esp_event_post(esp_event_base_t event_base,
int32_t event_id,
void* event_data,
size_t event_data_size,
TickType_t ticks_to_wait);
/**
* @brief Posts an event to the specified event loop. The event loop library keeps a copy of event_data and manages
* the copy's lifetime automatically (allocation + deletion); this ensures that the data the
* @brief Posts an event to the specified event loop. The event loop library keeps a copy of event_data and manages
* the copy's lifetime automatically (allocation + deletion); this ensures that the data the
* handler recieves is always valid.
*
* This function behaves in the same manner as esp_event_post_to, except the additional specification of the event loop
@ -256,73 +259,60 @@ esp_err_t esp_event_post(esp_event_base_t event_base,
* @param[in] ticks_to_wait number of ticks to block on a full event queue
*
* @note posting events from an ISR is not supported
*
* @return
*
* @return
* - ESP_OK: Success
* - ESP_ERR_TIMEOUT: Time to wait for event queue to unblock expired
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
* - ESP_ERR_INVALID_ARG: Invalid combination of event base and event id
* - Others: Fail
*/
esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
void* event_data,
size_t event_data_size,
esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop,
esp_event_base_t event_base,
int32_t event_id,
void* event_data,
size_t event_data_size,
TickType_t ticks_to_wait);
/**
* @brief Dumps statistics of all event loops.
*
* Dumps event loop info in the format:
*
*
@verbatim
event loop
event
handler
handler
event
handler
handler
event loop
event
handler
...
handler
handler
...
...
event loop
handler
handler
...
where:
event loop
format: address,name rx:total_recieved dr:total_dropped inv:total_number_of_invocations run:total_runtime
format: address,name rx:total_recieved dr:total_dropped
where:
address - memory address of the event loop
name - name of the event loop
name - name of the event loop, 'none' if no dedicated task
total_recieved - number of successfully posted events
total_number_of_invocations - total number of handler invocations performed so far
total_runtime - total runtime of all invocations so far
event
format: base:id proc:total_processed run:total_runtime
where:
base - event base
id - event id
total_processed - number of instances of this event that has been processed
total_runtime - total amount of time in microseconds used for invoking handlers of this event
total_dropped - number of events unsucessfully posted due to queue being full
handler
format: address inv:total_invoked run:total_runtime
format: address ev:base,id inv:total_invoked run:total_runtime
where:
address - address of the handler function
base,id - the event specified by event base and id this handler executes
total_invoked - number of times this handler has been invoked
total_runtime - total amount of time used for invoking this handler
@endverbatim
*
* @param[in] file the file stream to output to
*
* @note this function is a noop when CONFIG_EVENT_LOOP_PROFILING is disabled
*
* @return
* @return
* - ESP_OK: Success
* - ESP_ERR_NO_MEM: Cannot allocate memory for event loops list
* - Others: Fail

View file

@ -21,68 +21,66 @@
extern "C" {
#endif
typedef SLIST_HEAD(base_nodes, base_node) base_nodes_t;
/// Event handler
typedef struct esp_event_handler_instance {
esp_event_handler_t handler; /**< event handler function*/
void* arg; /**< event handler argument */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t total_times_invoked; /**< number of times this handler has been invoked */
int64_t total_runtime; /**< total runtime of this handler across all calls */
uint32_t invoked; /**< number of times this handler has been invoked */
int64_t time; /**< total runtime of this handler across all calls */
#endif
SLIST_ENTRY(esp_event_handler_instance) handler_entry; /**< next event handler in the list */
SLIST_ENTRY(esp_event_handler_instance) next; /**< next event handler in the list */
} esp_event_handler_instance_t;
typedef SLIST_HEAD(esp_event_handler_instances, esp_event_handler_instance) esp_event_handler_instances_t;
typedef struct esp_event_id_instance {
int32_t id;
esp_event_handler_instances_t handlers; /**< list of handlers to be executed when
this event is raised */
SLIST_ENTRY(esp_event_id_instance) event_id_entry; /**< pointer to the next event node on the linked list */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t handlers_invoked; /**< total number of times the event has been
raised and processed in the loop */
int64_t handlers_runtime; /**< total time spent in executing handlers */
#endif
} esp_event_id_instance_t;
typedef SLIST_HEAD(esp_event_id_instances, esp_event_id_instance) esp_event_id_instances_t;
/// Event
typedef struct esp_event_base_instance {
esp_event_base_t base; /**< base identifier of the event */
esp_event_handler_instances_t base_handlers; /**< event base level handlers, handlers for
all events with this base */
esp_event_id_instances_t event_ids; /**< list of event ids with this base */
SLIST_ENTRY(esp_event_base_instance) event_base_entry; /**< pointer to the next event node on the linked list */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t base_handlers_invoked; /**< total number of base-level handlers invoked */
int64_t base_handlers_runtime; /**< amount of time processing base-level handlers */
#endif
} esp_event_base_instance_t;
typedef struct esp_event_id_node {
int32_t id; /**< id number of the event */
esp_event_handler_instances_t handlers; /**< list of handlers to be executed when
this event is raised */
SLIST_ENTRY(esp_event_id_node) next; /**< pointer to the next event node on the linked list */
} esp_event_id_node_t;
typedef SLIST_HEAD(esp_event_base_instances, esp_event_base_instance) esp_event_base_instances_t;
typedef SLIST_HEAD(esp_event_id_nodes, esp_event_id_node) esp_event_id_nodes_t;
typedef struct esp_event_base_node {
esp_event_base_t base; /**< base identifier of the event */
esp_event_handler_instances_t handlers; /**< event base level handlers, handlers for
all events with this base */
esp_event_id_nodes_t id_nodes; /**< list of event ids with this base */
SLIST_ENTRY(esp_event_base_node) next; /**< pointer to the next base node on the linked list */
} esp_event_base_node_t;
typedef SLIST_HEAD(esp_event_base_nodes, esp_event_base_node) esp_event_base_nodes_t;
typedef struct esp_event_loop_node {
esp_event_handler_instances_t handlers; /** event loop level handlers */
esp_event_base_nodes_t base_nodes; /** list of event bases registered to the loop */
SLIST_ENTRY(esp_event_loop_node) next; /** pointer to the next loop node containing
event loop level handlers and the rest of
event bases registered to the loop */
} esp_event_loop_node_t;
typedef SLIST_HEAD(esp_event_loop_nodes, esp_event_loop_node) esp_event_loop_nodes_t;
/// Event loop
typedef struct esp_event_loop_instance {
const char* name; /**< name of this event loop */
QueueHandle_t queue; /**< event queue */
TaskHandle_t task; /**< task that consumes the event queue */
TaskHandle_t running_task; /**< for loops with no dedicated task, the
TaskHandle_t running_task; /**< for loops with no dedicated task, the
task that consumes the queue */
SemaphoreHandle_t mutex; /**< mutex for updating the events linked list */
esp_event_handler_instances_t loop_handlers; /**< loop level handlers, handlers for all events
registered in the loop */
esp_event_base_instances_t event_bases; /**< events linked list head pointer */
esp_event_loop_nodes_t loop_nodes; /**< set of linked lists containing the
registered handlers for the loop */
#ifdef CONFIG_EVENT_LOOP_PROFILING
uint32_t events_recieved; /**< number of events successfully posted to the loop */
uint32_t events_dropped; /**< number of events dropped due to queue being full */
uint32_t loop_handlers_invoked; /**< total number of loop-level handlers invoked */
int64_t loop_handlers_runtime; /**< amount of time processing loop-level handlers */
uint32_t total_handlers_invoked; /**< total number of handlers invoked */
int64_t total_handlers_runtime; /**< total amount of time dedicated to processing this loop */
SLIST_ENTRY(esp_event_loop_instance) loop_entry; /**< next event loop in the list */
SemaphoreHandle_t profiling_mutex;
SemaphoreHandle_t profiling_mutex; /**< mutex used for profiliing */
SLIST_ENTRY(esp_event_loop_instance) next; /**< next event loop in the list */
#endif
} esp_event_loop_instance_t;

View file

@ -74,6 +74,11 @@ typedef struct {
SemaphoreHandle_t mutex;
} simple_arg_t;
typedef struct {
int *arr;
int index;
} ordered_data_t;
static BaseType_t s_test_core_id;
static UBaseType_t s_test_priority;
@ -135,6 +140,13 @@ static void test_event_simple_handler(void* event_handler_arg, esp_event_base_t
xSemaphoreGive(arg->mutex);
}
static void test_event_ordered_dispatch(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
int *arg = (int*) event_handler_arg;
ordered_data_t *data = *((ordered_data_t**) (event_data));
data->arr[data->index++] = *arg;
}
static void test_event_performance_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
@ -193,7 +205,7 @@ static void test_handler_post_w_task(void* event_handler_arg, esp_event_base_t e
int* count = (int*) arg->data;
(*count)++;
if (*count <= 2) {
if (event_base == s_test_base1 && event_id == TEST_EVENT_BASE1_EV1) {
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY));
@ -218,7 +230,7 @@ static void test_handler_post_wo_task(void* event_handler_arg, esp_event_base_t
int* count = (int*) arg->data;
(*count)++;
if (*count <= 2) {
if (event_base == s_test_base1 && event_id == TEST_EVENT_BASE1_EV1) {
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY));
@ -846,7 +858,7 @@ TEST_CASE("can post to loop from handler - dedicated task", "[event]")
// Test that other tasks can still post while there is still slots in the queue, while handler is executing
count = 100;
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_w_task, sizeof(&loop_w_task), portMAX_DELAY));
for (int i = 0; i < loop_args.queue_size; i++) {
@ -854,7 +866,7 @@ TEST_CASE("can post to loop from handler - dedicated task", "[event]")
}
TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0,
pdMS_TO_TICKS(CONFIG_INT_WDT_TIMEOUT_MS * TEST_CONFIG_WAIT_MULTIPLIER)));
pdMS_TO_TICKS(CONFIG_INT_WDT_TIMEOUT_MS * TEST_CONFIG_WAIT_MULTIPLIER)));
xSemaphoreGive(arg.mutex);
@ -977,7 +989,7 @@ TEST_CASE("can register from handler", "[event]")
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_registration_from_handler_hdlr, &count));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV1, test_unregistration_from_handler_hdlr, &count));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &loop, sizeof(&loop), portMAX_DELAY));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_run(loop, pdMS_TO_TICKS(10)));
@ -1024,7 +1036,7 @@ TEST_CASE("can create and delete loop from handler", "[event]")
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_create_loop_handler, &test_loop));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_create_loop_handler, &test_loop));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_run(loop, pdMS_TO_TICKS(10)));
@ -1036,6 +1048,60 @@ TEST_CASE("can create and delete loop from handler", "[event]")
TEST_TEARDOWN();
}
TEST_CASE("events are dispatched in the order they are registered", "[event]")
{
TEST_SETUP();
esp_event_loop_handle_t loop;
esp_event_loop_args_t loop_args = test_event_get_default_loop_args();
loop_args.task_name = NULL;
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create(&loop_args, &loop));
int id_arr[7];
for (int i = 0; i < 7; i++) {
id_arr[i] = i;
}
int data_arr[12] = {0};
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV1, test_event_ordered_dispatch, id_arr + 0));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 1));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 2));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV2, test_event_ordered_dispatch, id_arr + 3));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_ordered_dispatch, id_arr + 4));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base2, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 5));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_ordered_dispatch, id_arr + 6));
esp_event_dump(stdout);
ordered_data_t data = {
.arr = data_arr,
.index = 0
};
ordered_data_t* dptr = &data;
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, &dptr, sizeof(dptr), portMAX_DELAY));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &dptr, sizeof(dptr), portMAX_DELAY));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, &dptr, sizeof(dptr), portMAX_DELAY));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, &dptr, sizeof(dptr), portMAX_DELAY));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_run(loop, pdMS_TO_TICKS(10)));
// Expected data executing the posts above
int ref_arr[12] = {1, 3, 5, 1, 2, 4, 1, 2, 6, 0, 1, 5};
for (int i = 0; i < 12; i++) {
TEST_ASSERT_EQUAL(ref_arr[i], data_arr[i]);
}
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete(loop));
TEST_TEARDOWN();
}
#ifdef CONFIG_EVENT_LOOP_PROFILING
TEST_CASE("can dump event loop profile", "[event]")
{
@ -1060,6 +1126,7 @@ TEST_CASE("can dump event loop profile", "[event]")
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler, &arg));

View file

@ -13,11 +13,11 @@ code execution to another context.
Using ``esp_event`` APIs
------------------------
There are two objects of concern for users of this library: events and event loops.
There are two objects of concern for users of this library: events and event loops.
Events are occurences of note. For example, for WiFi, a successful connection to the access point may be an event.
Events are referenced using a two part identifier which are discussed more :ref:`here <esp-event-declaring-defining-events>`.
Event loops are the vehicle by which events get posted by event sources and handled by event handler functions.
Events are occurences of note. For example, for WiFi, a successful connection to the access point may be an event.
Events are referenced using a two part identifier which are discussed more :ref:`here <esp-event-declaring-defining-events>`.
Event loops are the vehicle by which events get posted by event sources and handled by event handler functions.
These two appear prominently in the event loop library APIs.
Using this library roughly entails the following flow:
@ -41,7 +41,7 @@ In code, the flow above may look like as follows:
void app_main()
{
// 2. A configuration structure of type esp_event_loop_args_t is needed to specify the properties of the loop to be
// 2. A configuration structure of type esp_event_loop_args_t is needed to specify the properties of the loop to be
// created. A handle of type esp_event_loop_handle_t is obtained, which is needed by the other APIs to reference the loop
// to perform their operations on.
esp_event_loop_args_t loop_args = {
@ -56,8 +56,8 @@ In code, the flow above may look like as follows:
esp_event_loop_create(&loop_args, &loop_handle)
// 3. Register event handler defined in (1). MY_EVENT_BASE and MY_EVENT_ID specifies a hypothetical
// event that handler run_on_event should execute on when it gets posted to the loop.
// 3. Register event handler defined in (1). MY_EVENT_BASE and MY_EVENT_ID specifies a hypothetical
// event that handler run_on_event should execute on when it gets posted to the loop.
esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event, ...);
...
@ -127,29 +127,29 @@ Default Event Loop
------------------
The default event loop is a special type of loop used for system events (WiFi events, for example). The handle for this
loop is hidden from the user. The creation, deletion, handler registration/unregistration and posting of events is done
through a variant of the APIs for user event loops. The table below enumerates those variants, and the user event
loop is hidden from the user. The creation, deletion, handler registration/unregistration and posting of events is done
through a variant of the APIs for user event loops. The table below enumerates those variants, and the user event
loops equivalent.
+---------------------------------------------------+---------------------------------------------------+
| User Event Loops | Default Event Loops |
+===================================================+===================================================+
| :cpp:func:`esp_event_loop_create` | :cpp:func:`esp_event_loop_create_default` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_loop_delete` | :cpp:func:`esp_event_loop_delete_default` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_handler_register_with` | :cpp:func:`esp_event_handler_register` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_handler_unregister_with` | :cpp:func:`esp_event_handler_unregister` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_post_to` | :cpp:func:`esp_event_post` |
+---------------------------------------------------+---------------------------------------------------+
+---------------------------------------------------+---------------------------------------------------+
| User Event Loops | Default Event Loops |
+===================================================+===================================================+
| :cpp:func:`esp_event_loop_create` | :cpp:func:`esp_event_loop_create_default` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_loop_delete` | :cpp:func:`esp_event_loop_delete_default` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_handler_register_with` | :cpp:func:`esp_event_handler_register` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_handler_unregister_with` | :cpp:func:`esp_event_handler_unregister` |
+---------------------------------------------------+---------------------------------------------------+
| :cpp:func:`esp_event_post_to` | :cpp:func:`esp_event_post` |
+---------------------------------------------------+---------------------------------------------------+
If you compare the signatures for both, they are mostly similar except the for the lack of loop handle
specification for the default event loop APIs.
specification for the default event loop APIs.
Other than the API difference and the special designation to which system events are posted to, there is no difference
to how default event loops and user event loops behave. It is even possible for users to post their own events
to how default event loops and user event loops behave. It is even possible for users to post their own events
to the default event loop, should the user opt to not create their own loops to save memory.
.. _esp-event-handler-registration:
@ -186,6 +186,17 @@ If the hypothetical event ``MY_EVENT_BASE``, ``MY_OTHER_EVENT_ID`` is posted, on
If the hypothetical event ``MY_OTHER_EVENT_BASE``, ``MY_OTHER_EVENT_ID`` is posted, only ``run_on_event_3`` would execute.
Handler Registration and Handler Dispatch Order
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The general rule is that for handlers that match a certain posted event during dispatch, those which are registered first also gets executed first. The user can then control which handlers get executed first by
registering them before other handlers, provided that all registrations are performed using a single task.
If the user plans to take advantage of this behavior, caution must be exercised if there are multiple tasks registering handlers. While the 'first registered, first executed'
behavior still holds true, the task which gets executed first will also get their handlers registered first. Handlers registered one after the other by a single task
will still be dispatched in the order relative to each other, but if that task gets pre-empted in between registration by another task which also registers handlers; then during dispatch those
handlers will also get executed in between.
Event loop profiling
--------------------