Skip to content

Commit 82f558b

Browse files
committed
Merge branch 'fix/esp-event-profiling' into 'master'
fix(esp_event): Fix event loop profiling in handler_execute function Closes IDFGH-14245 See merge request espressif/esp-idf!35735
2 parents b5ee028 + 15f6775 commit 82f558b

File tree

4 files changed

+370
-76
lines changed

4 files changed

+370
-76
lines changed

components/esp_event/esp_event.c

Lines changed: 168 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -22,7 +22,7 @@
2222
/* ---------------------------- Definitions --------------------------------- */
2323

2424
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
25-
// LOOP @<address, name> rx:<recieved events no.> dr:<dropped events no.>
25+
// LOOP @<address, name> rx:<received events no.> dr:<dropped events no.>
2626
#define LOOP_DUMP_FORMAT "LOOP @%p,%s rx:%" PRIu32 " dr:%" PRIu32 "\n"
2727
// handler @<address> ev:<base, id> inv:<times invoked> time:<runtime>
2828
#define HANDLER_DUMP_FORMAT " HANDLER @%p ev:%s,%s inv:%" PRIu32 " time:%lld us\n"
@@ -38,6 +38,7 @@
3838

3939
static const char* TAG = "event";
4040
static const char* esp_event_any_base = "any";
41+
static const char* esp_event_handler_cleanup = "cleanup";
4142

4243
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
4344
static SLIST_HEAD(esp_event_loop_instance_list_t, esp_event_loop_instance) s_event_loops =
@@ -140,23 +141,8 @@ static void handler_execute(esp_event_loop_instance_t* loop, esp_event_handler_n
140141
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
141142
diff = esp_timer_get_time() - start;
142143

143-
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
144-
145-
// At this point handler may be already unregistered.
146-
// This happens in "handler instance can unregister itself" test case.
147-
// To prevent memory corruption error it's necessary to check if pointer is still valid.
148-
esp_event_loop_node_t* loop_node;
149-
esp_event_handler_node_t* handler_node;
150-
SLIST_FOREACH(loop_node, &(loop->loop_nodes), next) {
151-
SLIST_FOREACH(handler_node, &(loop_node->handlers), next) {
152-
if (handler_node == handler) {
153-
handler->invoked++;
154-
handler->time += diff;
155-
}
156-
}
157-
}
158-
159-
xSemaphoreGive(loop->profiling_mutex);
144+
handler->invoked++;
145+
handler->time += diff;
160146
#endif
161147
}
162148

@@ -355,8 +341,8 @@ static esp_err_t base_node_remove_handler(esp_event_base_node_t* base_node, int3
355341
if (SLIST_EMPTY(&(it->handlers))) {
356342
SLIST_REMOVE(&(base_node->id_nodes), it, esp_event_id_node, next);
357343
free(it);
358-
return ESP_OK;
359344
}
345+
return ESP_OK;
360346
}
361347
}
362348
}
@@ -379,8 +365,8 @@ static esp_err_t loop_node_remove_handler(esp_event_loop_node_t* loop_node, esp_
379365
if (SLIST_EMPTY(&(it->handlers)) && SLIST_EMPTY(&(it->id_nodes))) {
380366
SLIST_REMOVE(&(loop_node->base_nodes), it, esp_event_base_node, next);
381367
free(it);
382-
return ESP_OK;
383368
}
369+
return ESP_OK;
384370
}
385371
}
386372
}
@@ -389,6 +375,23 @@ static esp_err_t loop_node_remove_handler(esp_event_loop_node_t* loop_node, esp_
389375
return ESP_ERR_NOT_FOUND;
390376
}
391377

378+
static esp_err_t loop_remove_handler(esp_event_remove_handler_context_t* ctx)
379+
{
380+
esp_event_loop_node_t *it, *temp;
381+
SLIST_FOREACH_SAFE(it, &(ctx->loop->loop_nodes), next, temp) {
382+
esp_err_t res = loop_node_remove_handler(it, ctx->event_base, ctx->event_id, ctx->handler_ctx, ctx->legacy);
383+
384+
if (res == ESP_OK) {
385+
if (SLIST_EMPTY(&(it->base_nodes)) && SLIST_EMPTY(&(it->handlers))) {
386+
SLIST_REMOVE(&(ctx->loop->loop_nodes), it, esp_event_loop_node, next);
387+
free(it);
388+
}
389+
return ESP_OK;
390+
}
391+
}
392+
return ESP_ERR_NOT_FOUND;
393+
}
394+
392395
static void handler_instances_remove_all(esp_event_handler_nodes_t* handlers)
393396
{
394397
esp_event_handler_node_t *it, *temp;
@@ -437,6 +440,104 @@ static void inline __attribute__((always_inline)) post_instance_delete(esp_event
437440
memset(post, 0, sizeof(*post));
438441
}
439442

443+
static esp_err_t find_and_unregister_handler(esp_event_remove_handler_context_t* ctx)
444+
{
445+
esp_event_handler_node_t *handler_to_unregister = NULL;
446+
esp_event_handler_node_t *handler;
447+
esp_event_loop_node_t *loop_node;
448+
esp_event_base_node_t *base_node;
449+
esp_event_id_node_t *id_node;
450+
451+
SLIST_FOREACH(loop_node, &(ctx->loop->loop_nodes), next) {
452+
// Execute loop level handlers
453+
SLIST_FOREACH(handler, &(loop_node->handlers), next) {
454+
if (ctx->legacy) {
455+
if (handler->handler_ctx->handler == ctx->handler_ctx->handler) {
456+
handler_to_unregister = handler;
457+
break;
458+
}
459+
} else {
460+
if (handler->handler_ctx == ctx->handler_ctx) {
461+
handler_to_unregister = handler;
462+
break;
463+
}
464+
}
465+
}
466+
467+
if (handler_to_unregister != NULL) {
468+
break;
469+
}
470+
471+
SLIST_FOREACH(base_node, &(loop_node->base_nodes), next) {
472+
if (base_node->base == ctx->event_base) {
473+
// Execute base level handlers
474+
SLIST_FOREACH(handler, &(base_node->handlers), next) {
475+
if (ctx->legacy) {
476+
if (handler->handler_ctx->handler == ctx->handler_ctx->handler) {
477+
handler_to_unregister = handler;
478+
break;
479+
}
480+
} else {
481+
if (handler->handler_ctx == ctx->handler_ctx) {
482+
handler_to_unregister = handler;
483+
break;
484+
}
485+
}
486+
}
487+
488+
if (handler_to_unregister != NULL) {
489+
break;
490+
}
491+
492+
SLIST_FOREACH(id_node, &(base_node->id_nodes), next) {
493+
if (id_node->id == ctx->event_id) {
494+
// Execute id level handlers
495+
SLIST_FOREACH(handler, &(id_node->handlers), next) {
496+
if (ctx->legacy) {
497+
if (handler->handler_ctx->handler == ctx->handler_ctx->handler) {
498+
handler_to_unregister = handler;
499+
break;
500+
}
501+
} else {
502+
if (handler->handler_ctx == ctx->handler_ctx) {
503+
handler_to_unregister = handler;
504+
break;
505+
}
506+
}
507+
}
508+
}
509+
}
510+
}
511+
}
512+
}
513+
514+
if (handler_to_unregister == NULL) {
515+
/* handler not found in the lists, return */
516+
return ESP_ERR_NOT_FOUND;
517+
}
518+
519+
if (handler_to_unregister->unregistered) {
520+
/* the handler was found in a list but has already be marked
521+
* as unregistered. It means an event was already created to
522+
* remove from the list. return OK but do nothing */
523+
return ESP_OK;
524+
}
525+
/* handler found in the lists and not already marked as unregistered. Mark it as unregistered
526+
* and post an event to remove it from the lists */
527+
handler_to_unregister->unregistered = true;
528+
if (ctx->legacy) {
529+
/* in case of legacy code, we have to copy the handler_ctx content since it was created in the calling function */
530+
esp_event_handler_instance_context_t *handler_ctx_copy = calloc(1, sizeof(esp_event_handler_instance_context_t));
531+
if (!handler_ctx_copy) {
532+
return ESP_ERR_NO_MEM;
533+
}
534+
handler_ctx_copy->arg = ctx->handler_ctx->arg;
535+
handler_ctx_copy->handler = ctx->handler_ctx->handler;
536+
ctx->handler_ctx = handler_ctx_copy;
537+
}
538+
return esp_event_post_to(ctx->loop, esp_event_handler_cleanup, 0, ctx, sizeof(esp_event_remove_handler_context_t), portMAX_DELAY);
539+
}
540+
440541
/* ---------------------------- Public API --------------------------------- */
441542

442543
esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, esp_event_loop_handle_t* event_loop)
@@ -472,14 +573,6 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
472573
goto on_err;
473574
}
474575

475-
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
476-
loop->profiling_mutex = xSemaphoreCreateMutex();
477-
if (loop->profiling_mutex == NULL) {
478-
ESP_LOGE(TAG, "create event loop profiling mutex failed");
479-
goto on_err;
480-
}
481-
#endif
482-
483576
SLIST_INIT(&(loop->loop_nodes));
484577

485578
// Create the loop task if requested
@@ -525,12 +618,6 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
525618
vSemaphoreDelete(loop->mutex);
526619
}
527620

528-
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
529-
if (loop->profiling_mutex != NULL) {
530-
vSemaphoreDelete(loop->profiling_mutex);
531-
}
532-
#endif
533-
534621
free(loop);
535622

536623
return err;
@@ -557,10 +644,25 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
557644
int64_t remaining_ticks = ticks_to_run;
558645
#endif
559646

560-
while (xQueueReceive(loop->queue, &post, ticks_to_run) == pdTRUE) {
647+
while (xQueueReceive(loop->queue, &post, remaining_ticks) == pdTRUE) {
561648
// The event has already been unqueued, so ensure it gets executed.
562649
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
563650

651+
// check if the event retrieve from the queue is the internal event that is
652+
// triggered when a handler needs to be removed..
653+
if (post.base == esp_event_handler_cleanup) {
654+
assert(post.data.ptr != NULL);
655+
esp_event_remove_handler_context_t* ctx = (esp_event_remove_handler_context_t*)post.data.ptr;
656+
loop_remove_handler(ctx);
657+
658+
// if the handler unregistration request came from legacy code,
659+
// we have to free handler_ctx pointer since it points to memory
660+
// allocated by esp_event_handler_unregister_with_internal
661+
if (ctx->legacy) {
662+
free(ctx->handler_ctx);
663+
}
664+
}
665+
564666
loop->running_task = xTaskGetCurrentTaskHandle();
565667

566668
bool exec = false;
@@ -573,24 +675,30 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
573675
SLIST_FOREACH_SAFE(loop_node, &(loop->loop_nodes), next, temp_node) {
574676
// Execute loop level handlers
575677
SLIST_FOREACH_SAFE(handler, &(loop_node->handlers), next, temp_handler) {
576-
handler_execute(loop, handler, post);
577-
exec |= true;
678+
if (!handler->unregistered) {
679+
handler_execute(loop, handler, post);
680+
exec |= true;
681+
}
578682
}
579683

580684
SLIST_FOREACH_SAFE(base_node, &(loop_node->base_nodes), next, temp_base) {
581685
if (base_node->base == post.base) {
582686
// Execute base level handlers
583687
SLIST_FOREACH_SAFE(handler, &(base_node->handlers), next, temp_handler) {
584-
handler_execute(loop, handler, post);
585-
exec |= true;
688+
if (!handler->unregistered) {
689+
handler_execute(loop, handler, post);
690+
exec |= true;
691+
}
586692
}
587693

588694
SLIST_FOREACH_SAFE(id_node, &(base_node->id_nodes), next, temp_id_node) {
589695
if (id_node->id == post.id) {
590696
// Execute id level handlers
591697
SLIST_FOREACH_SAFE(handler, &(id_node->handlers), next, temp_handler) {
592-
handler_execute(loop, handler, post);
593-
exec |= true;
698+
if (!handler->unregistered) {
699+
handler_execute(loop, handler, post);
700+
exec |= true;
701+
}
594702
}
595703
// Skip to next base node
596704
break;
@@ -637,14 +745,10 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
637745

638746
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
639747
SemaphoreHandle_t loop_mutex = loop->mutex;
640-
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
641-
SemaphoreHandle_t loop_profiling_mutex = loop->profiling_mutex;
642-
#endif
643748

644749
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
645750

646751
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
647-
xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
648752
portENTER_CRITICAL(&s_event_loops_spinlock);
649753
SLIST_REMOVE(&s_event_loops, loop, esp_event_loop_instance, next);
650754
portEXIT_CRITICAL(&s_event_loops_spinlock);
@@ -674,10 +778,6 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
674778
free(loop);
675779
// Free loop mutex before deleting
676780
xSemaphoreGiveRecursive(loop_mutex);
677-
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
678-
xSemaphoreGive(loop_profiling_mutex);
679-
vSemaphoreDelete(loop_profiling_mutex);
680-
#endif
681781
vSemaphoreDelete(loop_mutex);
682782

683783
return ESP_OK;
@@ -775,24 +875,21 @@ esp_err_t esp_event_handler_unregister_with_internal(esp_event_loop_handle_t eve
775875
}
776876

777877
esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
778-
779-
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
780-
781-
esp_event_loop_node_t *it, *temp;
782-
783-
SLIST_FOREACH_SAFE(it, &(loop->loop_nodes), next, temp) {
784-
esp_err_t res = loop_node_remove_handler(it, event_base, event_id, handler_ctx, legacy);
785-
786-
if (res == ESP_OK && SLIST_EMPTY(&(it->base_nodes)) && SLIST_EMPTY(&(it->handlers))) {
787-
SLIST_REMOVE(&(loop->loop_nodes), it, esp_event_loop_node, next);
788-
free(it);
789-
break;
790-
}
878+
esp_event_remove_handler_context_t remove_handler_ctx = {loop, event_base, event_id, handler_ctx, legacy};
879+
880+
/* remove the handler if the mutex is taken successfully.
881+
* otherwise it will be removed from the list later */
882+
esp_err_t res = ESP_FAIL;
883+
if (xSemaphoreTake(loop->mutex, 0) == pdTRUE) {
884+
res = loop_remove_handler(&remove_handler_ctx);
885+
xSemaphoreGive(loop->mutex);
886+
} else {
887+
xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
888+
res = find_and_unregister_handler(&remove_handler_ctx);
889+
xSemaphoreGiveRecursive(loop->mutex);
791890
}
792891

793-
xSemaphoreGiveRecursive(loop->mutex);
794-
795-
return ESP_OK;
892+
return res;
796893
}
797894

798895
esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
@@ -885,7 +982,7 @@ esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop, esp_event_base_t
885982
}
886983

887984
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
888-
atomic_fetch_add(&loop->events_recieved, 1);
985+
atomic_fetch_add(&loop->events_received, 1);
889986
#endif
890987

891988
return ESP_OK;
@@ -933,7 +1030,7 @@ esp_err_t esp_event_isr_post_to(esp_event_loop_handle_t event_loop, esp_event_ba
9331030
}
9341031

9351032
#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
936-
atomic_fetch_add(&loop->events_recieved, 1);
1033+
atomic_fetch_add(&loop->events_received, 1);
9371034
#endif
9381035

9391036
return ESP_OK;
@@ -962,13 +1059,13 @@ esp_err_t esp_event_dump(FILE* file)
9621059
portENTER_CRITICAL(&s_event_loops_spinlock);
9631060

9641061
SLIST_FOREACH(loop_it, &s_event_loops, next) {
965-
uint32_t events_recieved, events_dropped;
1062+
uint32_t events_received, events_dropped;
9661063

967-
events_recieved = atomic_load(&loop_it->events_recieved);
1064+
events_received = atomic_load(&loop_it->events_received);
9681065
events_dropped = atomic_load(&loop_it->events_dropped);
9691066

9701067
PRINT_DUMP_INFO(dst, sz, LOOP_DUMP_FORMAT, loop_it, loop_it->task != NULL ? loop_it->name : "none",
971-
events_recieved, events_dropped);
1068+
events_received, events_dropped);
9721069

9731070
int sz_bak = sz;
9741071

0 commit comments

Comments
 (0)