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 */
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,51 +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- SLIST_FOREACH (loop_node , & (loop -> loop_nodes ), next ) {
150- // try to find the handler amongst the list of handlers that are registered on the
151- // loop level (i.e., registered to be triggered for all events)
152- esp_event_handler_node_t * handler_node ;
153- SLIST_FOREACH (handler_node , & (loop_node -> handlers ), next ) {
154- if (handler_node == handler ) {
155- handler -> invoked ++ ;
156- handler -> time += diff ;
157- }
158- }
159-
160- // try to find the handler amongst the list of handlers that are registered on the
161- // base level (i.e., registered to be triggered for all events of a specific event base)
162- esp_event_base_node_t * base_node ;
163- SLIST_FOREACH (base_node , & (loop_node -> base_nodes ), next ) {
164- esp_event_handler_node_t * base_handler_node ;
165- SLIST_FOREACH (base_handler_node , & (base_node -> handlers ), next ) {
166- if (base_handler_node == handler ) {
167- handler -> invoked ++ ;
168- handler -> time += diff ;
169- }
170- }
171-
172- // try to find the handler amongst the list of handlers that are registered on the
173- // ID level (i.e., registered to be triggered for a specific event ID of a specific event base)
174- esp_event_id_node_t * id_node ;
175- SLIST_FOREACH (id_node , & (base_node -> id_nodes ), next ) {
176- esp_event_handler_node_t * id_handler_node ;
177- SLIST_FOREACH (id_handler_node , & (id_node -> handlers ), next ) {
178- if (id_handler_node == handler ) {
179- handler -> invoked ++ ;
180- handler -> time += diff ;
181- }
182- }
183- }
184- }
185- }
186-
187- xSemaphoreGive (loop -> profiling_mutex );
144+ handler -> invoked ++ ;
145+ handler -> time += diff ;
188146#endif
189147}
190148
@@ -383,8 +341,8 @@ static esp_err_t base_node_remove_handler(esp_event_base_node_t* base_node, int3
383341 if (SLIST_EMPTY (& (it -> handlers ))) {
384342 SLIST_REMOVE (& (base_node -> id_nodes ), it , esp_event_id_node , next );
385343 free (it );
386- return ESP_OK ;
387344 }
345+ return ESP_OK ;
388346 }
389347 }
390348 }
@@ -407,8 +365,8 @@ static esp_err_t loop_node_remove_handler(esp_event_loop_node_t* loop_node, esp_
407365 if (SLIST_EMPTY (& (it -> handlers )) && SLIST_EMPTY (& (it -> id_nodes ))) {
408366 SLIST_REMOVE (& (loop_node -> base_nodes ), it , esp_event_base_node , next );
409367 free (it );
410- return ESP_OK ;
411368 }
369+ return ESP_OK ;
412370 }
413371 }
414372 }
@@ -417,6 +375,23 @@ static esp_err_t loop_node_remove_handler(esp_event_loop_node_t* loop_node, esp_
417375 return ESP_ERR_NOT_FOUND ;
418376}
419377
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+
420395static void handler_instances_remove_all (esp_event_handler_nodes_t * handlers )
421396{
422397 esp_event_handler_node_t * it , * temp ;
@@ -465,6 +440,104 @@ static void inline __attribute__((always_inline)) post_instance_delete(esp_event
465440 memset (post , 0 , sizeof (* post ));
466441}
467442
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+
468541/* ---------------------------- Public API --------------------------------- */
469542
470543esp_err_t esp_event_loop_create (const esp_event_loop_args_t * event_loop_args , esp_event_loop_handle_t * event_loop )
@@ -500,14 +573,6 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
500573 goto on_err ;
501574 }
502575
503- #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
504- loop -> profiling_mutex = xSemaphoreCreateMutex ();
505- if (loop -> profiling_mutex == NULL ) {
506- ESP_LOGE (TAG , "create event loop profiling mutex failed" );
507- goto on_err ;
508- }
509- #endif
510-
511576 SLIST_INIT (& (loop -> loop_nodes ));
512577
513578 // Create the loop task if requested
@@ -553,12 +618,6 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
553618 vSemaphoreDelete (loop -> mutex );
554619 }
555620
556- #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
557- if (loop -> profiling_mutex != NULL ) {
558- vSemaphoreDelete (loop -> profiling_mutex );
559- }
560- #endif
561-
562621 free (loop );
563622
564623 return err ;
@@ -585,10 +644,25 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
585644 int64_t remaining_ticks = ticks_to_run ;
586645#endif
587646
588- while (xQueueReceive (loop -> queue , & post , ticks_to_run ) == pdTRUE ) {
647+ while (xQueueReceive (loop -> queue , & post , remaining_ticks ) == pdTRUE ) {
589648 // The event has already been unqueued, so ensure it gets executed.
590649 xSemaphoreTakeRecursive (loop -> mutex , portMAX_DELAY );
591650
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+
592666 loop -> running_task = xTaskGetCurrentTaskHandle ();
593667
594668 bool exec = false;
@@ -601,24 +675,30 @@ esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t tick
601675 SLIST_FOREACH_SAFE (loop_node , & (loop -> loop_nodes ), next , temp_node ) {
602676 // Execute loop level handlers
603677 SLIST_FOREACH_SAFE (handler , & (loop_node -> handlers ), next , temp_handler ) {
604- handler_execute (loop , handler , post );
605- exec |= true;
678+ if (!handler -> unregistered ) {
679+ handler_execute (loop , handler , post );
680+ exec |= true;
681+ }
606682 }
607683
608684 SLIST_FOREACH_SAFE (base_node , & (loop_node -> base_nodes ), next , temp_base ) {
609685 if (base_node -> base == post .base ) {
610686 // Execute base level handlers
611687 SLIST_FOREACH_SAFE (handler , & (base_node -> handlers ), next , temp_handler ) {
612- handler_execute (loop , handler , post );
613- exec |= true;
688+ if (!handler -> unregistered ) {
689+ handler_execute (loop , handler , post );
690+ exec |= true;
691+ }
614692 }
615693
616694 SLIST_FOREACH_SAFE (id_node , & (base_node -> id_nodes ), next , temp_id_node ) {
617695 if (id_node -> id == post .id ) {
618696 // Execute id level handlers
619697 SLIST_FOREACH_SAFE (handler , & (id_node -> handlers ), next , temp_handler ) {
620- handler_execute (loop , handler , post );
621- exec |= true;
698+ if (!handler -> unregistered ) {
699+ handler_execute (loop , handler , post );
700+ exec |= true;
701+ }
622702 }
623703 // Skip to next base node
624704 break ;
@@ -665,14 +745,10 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
665745
666746 esp_event_loop_instance_t * loop = (esp_event_loop_instance_t * ) event_loop ;
667747 SemaphoreHandle_t loop_mutex = loop -> mutex ;
668- #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
669- SemaphoreHandle_t loop_profiling_mutex = loop -> profiling_mutex ;
670- #endif
671748
672749 xSemaphoreTakeRecursive (loop -> mutex , portMAX_DELAY );
673750
674751#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
675- xSemaphoreTake (loop -> profiling_mutex , portMAX_DELAY );
676752 portENTER_CRITICAL (& s_event_loops_spinlock );
677753 SLIST_REMOVE (& s_event_loops , loop , esp_event_loop_instance , next );
678754 portEXIT_CRITICAL (& s_event_loops_spinlock );
@@ -702,10 +778,6 @@ esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
702778 free (loop );
703779 // Free loop mutex before deleting
704780 xSemaphoreGiveRecursive (loop_mutex );
705- #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
706- xSemaphoreGive (loop_profiling_mutex );
707- vSemaphoreDelete (loop_profiling_mutex );
708- #endif
709781 vSemaphoreDelete (loop_mutex );
710782
711783 return ESP_OK ;
@@ -803,24 +875,21 @@ esp_err_t esp_event_handler_unregister_with_internal(esp_event_loop_handle_t eve
803875 }
804876
805877 esp_event_loop_instance_t * loop = (esp_event_loop_instance_t * ) event_loop ;
806-
807- xSemaphoreTakeRecursive (loop -> mutex , portMAX_DELAY );
808-
809- esp_event_loop_node_t * it , * temp ;
810-
811- SLIST_FOREACH_SAFE (it , & (loop -> loop_nodes ), next , temp ) {
812- esp_err_t res = loop_node_remove_handler (it , event_base , event_id , handler_ctx , legacy );
813-
814- if (res == ESP_OK && SLIST_EMPTY (& (it -> base_nodes )) && SLIST_EMPTY (& (it -> handlers ))) {
815- SLIST_REMOVE (& (loop -> loop_nodes ), it , esp_event_loop_node , next );
816- free (it );
817- break ;
818- }
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 );
819890 }
820891
821- xSemaphoreGiveRecursive (loop -> mutex );
822-
823- return ESP_OK ;
892+ return res ;
824893}
825894
826895esp_err_t esp_event_handler_unregister_with (esp_event_loop_handle_t event_loop , esp_event_base_t event_base ,
0 commit comments