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 */
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"
3838
3939static const char * TAG = "event" ;
4040static const char * esp_event_any_base = "any" ;
41+ static const char * esp_event_handler_cleanup = "cleanup" ;
4142
4243#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
4344static 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+
392395static 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
442543esp_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
798895esp_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