1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdbool.h>
19
20 #include "esp_log.h"
21
22 #include "esp_event.h"
23 #include "esp_event_internal.h"
24 #include "esp_event_private.h"
25
26 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
27 #include "esp_timer.h"
28 #endif
29
30 /* ---------------------------- Definitions --------------------------------- */
31
32 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
33 // LOOP @<address, name> rx:<recieved events no.> dr:<dropped events no.>
34 #define LOOP_DUMP_FORMAT "LOOP @%p,%s rx:%u dr:%u\n"
35 // handler @<address> ev:<base, id> inv:<times invoked> time:<runtime>
36 #define HANDLER_DUMP_FORMAT " HANDLER @%p ev:%s,%s inv:%u time:%lld us\n"
37
38 #define PRINT_DUMP_INFO(dst, sz, ...) do { \
39 int cb = snprintf(dst, sz, __VA_ARGS__); \
40 dst += cb; \
41 sz -= cb; \
42 } while(0);
43 #endif
44
45 /* ------------------------- Static Variables ------------------------------- */
46
47 static const char* TAG = "event";
48 static const char* esp_event_any_base = "any";
49
50 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
51 static SLIST_HEAD(esp_event_loop_instance_list_t, esp_event_loop_instance) s_event_loops =
52 SLIST_HEAD_INITIALIZER(s_event_loops);
53
54 static portMUX_TYPE s_event_loops_spinlock = portMUX_INITIALIZER_UNLOCKED;
55 #endif
56
57
58 /* ------------------------- Static Functions ------------------------------- */
59
60 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
61
62
esp_event_dump_prepare(void)63 static int esp_event_dump_prepare(void)
64 {
65 esp_event_loop_instance_t* loop_it;
66 esp_event_loop_node_t *loop_node_it;
67 esp_event_base_node_t* base_node_it;
68 esp_event_id_node_t* id_node_it;
69 esp_event_handler_node_t* handler_it;
70
71 // Count the number of items to be printed. This is needed to compute how much memory to reserve.
72 int loops = 0, handlers = 0;
73
74 portENTER_CRITICAL(&s_event_loops_spinlock);
75
76 SLIST_FOREACH(loop_it, &s_event_loops, next) {
77 SLIST_FOREACH(loop_node_it, &(loop_it->loop_nodes), next) {
78 SLIST_FOREACH(handler_it, &(loop_node_it->handlers), next) {
79 handlers++;
80 }
81
82 SLIST_FOREACH(base_node_it, &(loop_node_it->base_nodes), next) {
83 SLIST_FOREACH(handler_it, &(base_node_it->handlers), next) {
84 handlers++;
85 }
86 SLIST_FOREACH(id_node_it, &(base_node_it->id_nodes), next) {
87 SLIST_FOREACH(handler_it, &(id_node_it->handlers), next) {
88 handlers++;
89 }
90 }
91 }
92 }
93 loops++;
94 }
95
96 portEXIT_CRITICAL(&s_event_loops_spinlock);
97
98 // Reserve slightly more memory than computed
99 int allowance = 3;
100 int size = (((loops + allowance) * (sizeof(LOOP_DUMP_FORMAT) + 10 + 20 + 2 * 11)) +
101 ((handlers + allowance) * (sizeof(HANDLER_DUMP_FORMAT) + 10 + 2 * 20 + 11 + 20)));
102
103 return size;
104 }
105 #endif
106
esp_event_loop_run_task(void * args)107 static void esp_event_loop_run_task(void* args)
108 {
109 esp_err_t err;
110 esp_event_loop_handle_t event_loop = (esp_event_loop_handle_t) args;
111
112 ESP_LOGD(TAG, "running task for loop %p", event_loop);
113
114 while(1) {
115 err = esp_event_loop_run(event_loop, portMAX_DELAY);
116 if (err != ESP_OK) {
117 break;
118 }
119 }
120
121 ESP_LOGE(TAG, "suspended task for loop %p", event_loop);
122 vTaskSuspend(NULL);
123 }
124
handler_execute(esp_event_loop_instance_t * loop,esp_event_handler_node_t * handler,esp_event_post_instance_t post)125 static void handler_execute(esp_event_loop_instance_t* loop, esp_event_handler_node_t *handler, esp_event_post_instance_t post)
126 {
127 ESP_LOGD(TAG, "running post %s:%d with handler %p and context %p on loop %p", post.base, post.id, handler->handler_ctx->handler, &handler->handler_ctx, loop);
128
129 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
130 int64_t start, diff;
131 start = esp_timer_get_time();
132 #endif
133 // Execute the handler
134 #if CONFIG_ESP_EVENT_POST_FROM_ISR
135 void* data_ptr = NULL;
136
137 if (post.data_set) {
138 if (post.data_allocated) {
139 data_ptr = post.data.ptr;
140 } else {
141 data_ptr = &post.data.val;
142 }
143 }
144
145 (*(handler->handler_ctx->handler))(handler->handler_ctx->arg, post.base, post.id, data_ptr);
146 #else
147 (*(handler->handler_ctx->handler))(handler->handler_ctx->arg, post.base, post.id, post.data);
148 #endif
149
150 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
151 diff = esp_timer_get_time() - start;
152
153 xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
154
155 handler->invoked++;
156 handler->time += diff;
157
158 xSemaphoreGive(loop->profiling_mutex);
159 #endif
160 }
161
handler_instances_add(esp_event_handler_nodes_t * handlers,esp_event_handler_t event_handler,void * event_handler_arg,esp_event_handler_instance_context_t ** handler_ctx,bool legacy)162 static esp_err_t handler_instances_add(esp_event_handler_nodes_t* handlers, esp_event_handler_t event_handler, void* event_handler_arg, esp_event_handler_instance_context_t **handler_ctx, bool legacy)
163 {
164 esp_event_handler_node_t *handler_instance = calloc(1, sizeof(*handler_instance));
165
166 if (!handler_instance) return ESP_ERR_NO_MEM;
167
168 esp_event_handler_instance_context_t *context = calloc(1, sizeof(*context));
169
170 if (!context) {
171 free(handler_instance);
172 return ESP_ERR_NO_MEM;
173 }
174
175 context->handler = event_handler;
176 context->arg = event_handler_arg;
177 handler_instance->handler_ctx = context;
178
179 if (SLIST_EMPTY(handlers)) {
180 SLIST_INSERT_HEAD(handlers, handler_instance, next);
181 }
182 else {
183 esp_event_handler_node_t *it = NULL, *last = NULL;
184
185 SLIST_FOREACH(it, handlers, next) {
186 if (legacy) {
187 if(event_handler == it->handler_ctx->handler) {
188 it->handler_ctx->arg = event_handler_arg;
189 ESP_LOGW(TAG, "handler already registered, overwriting");
190 free(handler_instance);
191 free(context);
192 return ESP_OK;
193 }
194 }
195 last = it;
196 }
197
198 SLIST_INSERT_AFTER(last, handler_instance, next);
199 }
200
201 // If the caller didn't provide the handler instance context, don't set it.
202 // It will be removed once the event loop is deleted.
203 if (handler_ctx) {
204 *handler_ctx = context;
205 }
206
207 return ESP_OK;
208 }
209
base_node_add_handler(esp_event_base_node_t * base_node,int32_t id,esp_event_handler_t event_handler,void * event_handler_arg,esp_event_handler_instance_context_t ** handler_ctx,bool legacy)210 static esp_err_t base_node_add_handler(esp_event_base_node_t* base_node,
211 int32_t id,
212 esp_event_handler_t event_handler,
213 void *event_handler_arg,
214 esp_event_handler_instance_context_t **handler_ctx,
215 bool legacy)
216 {
217 if (id == ESP_EVENT_ANY_ID) {
218 return handler_instances_add(&(base_node->handlers), event_handler, event_handler_arg, handler_ctx, legacy);
219 }
220 else {
221 esp_err_t err = ESP_OK;
222 esp_event_id_node_t *it = NULL, *id_node = NULL, *last_id_node = NULL;
223
224 SLIST_FOREACH(it, &(base_node->id_nodes), next) {
225 if (it->id == id) {
226 id_node = it;
227 }
228 last_id_node = it;
229 }
230
231 if (!last_id_node || !id_node) {
232 id_node = (esp_event_id_node_t*) calloc(1, sizeof(*id_node));
233
234 if (!id_node) {
235 ESP_LOGE(TAG, "alloc for new id node failed");
236 return ESP_ERR_NO_MEM;
237 }
238
239 id_node->id = id;
240
241 SLIST_INIT(&(id_node->handlers));
242
243 err = handler_instances_add(&(id_node->handlers), event_handler, event_handler_arg, handler_ctx, legacy);
244
245 if (err == ESP_OK) {
246 if (!last_id_node) {
247 SLIST_INSERT_HEAD(&(base_node->id_nodes), id_node, next);
248 }
249 else {
250 SLIST_INSERT_AFTER(last_id_node, id_node, next);
251 }
252 } else {
253 free(id_node);
254 }
255
256 return err;
257 }
258 else {
259 return handler_instances_add(&(id_node->handlers), event_handler, event_handler_arg, handler_ctx, legacy);
260 }
261 }
262 }
263
loop_node_add_handler(esp_event_loop_node_t * loop_node,esp_event_base_t base,int32_t id,esp_event_handler_t event_handler,void * event_handler_arg,esp_event_handler_instance_context_t ** handler_ctx,bool legacy)264 static esp_err_t loop_node_add_handler(esp_event_loop_node_t* loop_node,
265 esp_event_base_t base,
266 int32_t id,
267 esp_event_handler_t event_handler,
268 void *event_handler_arg,
269 esp_event_handler_instance_context_t **handler_ctx,
270 bool legacy)
271 {
272 if (base == esp_event_any_base && id == ESP_EVENT_ANY_ID) {
273 return handler_instances_add(&(loop_node->handlers), event_handler, event_handler_arg, handler_ctx, legacy);
274 }
275 else {
276 esp_err_t err = ESP_OK;
277 esp_event_base_node_t *it = NULL, *base_node = NULL, *last_base_node = NULL;
278
279 SLIST_FOREACH(it, &(loop_node->base_nodes), next) {
280 if (it->base == base) {
281 base_node = it;
282 }
283 last_base_node = it;
284 }
285
286 if (!last_base_node ||
287 !base_node ||
288 (base_node && !SLIST_EMPTY(&(base_node->id_nodes)) && id == ESP_EVENT_ANY_ID) ||
289 (last_base_node && last_base_node->base != base && !SLIST_EMPTY(&(last_base_node->id_nodes)) && id == ESP_EVENT_ANY_ID)) {
290 base_node = (esp_event_base_node_t*) calloc(1, sizeof(*base_node));
291
292 if (!base_node) {
293 ESP_LOGE(TAG, "alloc mem for new base node failed");
294 return ESP_ERR_NO_MEM;
295 }
296
297 base_node->base = base;
298
299 SLIST_INIT(&(base_node->handlers));
300 SLIST_INIT(&(base_node->id_nodes));
301
302 err = base_node_add_handler(base_node, id, event_handler, event_handler_arg, handler_ctx, legacy);
303
304 if (err == ESP_OK) {
305 if (!last_base_node) {
306 SLIST_INSERT_HEAD(&(loop_node->base_nodes), base_node, next);
307 }
308 else {
309 SLIST_INSERT_AFTER(last_base_node, base_node, next);
310 }
311 } else {
312 free(base_node);
313 }
314
315 return err;
316 } else {
317 return base_node_add_handler(base_node, id, event_handler, event_handler_arg, handler_ctx, legacy);
318 }
319 }
320 }
321
handler_instances_remove(esp_event_handler_nodes_t * handlers,esp_event_handler_instance_context_t * handler_ctx,bool legacy)322 static esp_err_t handler_instances_remove(esp_event_handler_nodes_t* handlers, esp_event_handler_instance_context_t* handler_ctx, bool legacy)
323 {
324 esp_event_handler_node_t *it, *temp;
325
326 SLIST_FOREACH_SAFE(it, handlers, next, temp) {
327 if (legacy) {
328 if (it->handler_ctx->handler == handler_ctx->handler) {
329 SLIST_REMOVE(handlers, it, esp_event_handler_node, next);
330 free(it->handler_ctx);
331 free(it);
332 return ESP_OK;
333 }
334 } else {
335 if (it->handler_ctx == handler_ctx) {
336 SLIST_REMOVE(handlers, it, esp_event_handler_node, next);
337 free(it->handler_ctx);
338 free(it);
339 return ESP_OK;
340 }
341 }
342 }
343
344 return ESP_ERR_NOT_FOUND;
345 }
346
347
base_node_remove_handler(esp_event_base_node_t * base_node,int32_t id,esp_event_handler_instance_context_t * handler_ctx,bool legacy)348 static esp_err_t base_node_remove_handler(esp_event_base_node_t* base_node, int32_t id, esp_event_handler_instance_context_t* handler_ctx, bool legacy)
349 {
350 if (id == ESP_EVENT_ANY_ID) {
351 return handler_instances_remove(&(base_node->handlers), handler_ctx, legacy);
352 }
353 else {
354 esp_event_id_node_t *it, *temp;
355 SLIST_FOREACH_SAFE(it, &(base_node->id_nodes), next, temp) {
356 if (it->id == id) {
357 esp_err_t res = handler_instances_remove(&(it->handlers), handler_ctx, legacy);
358
359 if (res == ESP_OK) {
360 if (SLIST_EMPTY(&(it->handlers))) {
361 SLIST_REMOVE(&(base_node->id_nodes), it, esp_event_id_node, next);
362 free(it);
363 return ESP_OK;
364 }
365 }
366 }
367 }
368 }
369
370 return ESP_ERR_NOT_FOUND;
371 }
372
loop_node_remove_handler(esp_event_loop_node_t * loop_node,esp_event_base_t base,int32_t id,esp_event_handler_instance_context_t * handler_ctx,bool legacy)373 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_instance_context_t* handler_ctx, bool legacy)
374 {
375 if (base == esp_event_any_base && id == ESP_EVENT_ANY_ID) {
376 return handler_instances_remove(&(loop_node->handlers), handler_ctx, legacy);
377 }
378 else {
379 esp_event_base_node_t *it, *temp;
380 SLIST_FOREACH_SAFE(it, &(loop_node->base_nodes), next, temp) {
381 if (it->base == base) {
382 esp_err_t res = base_node_remove_handler(it, id, handler_ctx, legacy);
383
384 if (res == ESP_OK) {
385 if (SLIST_EMPTY(&(it->handlers)) && SLIST_EMPTY(&(it->id_nodes))) {
386 SLIST_REMOVE(&(loop_node->base_nodes), it, esp_event_base_node, next);
387 free(it);
388 return ESP_OK;
389 }
390 }
391 }
392 }
393 }
394
395 return ESP_ERR_NOT_FOUND;
396 }
397
handler_instances_remove_all(esp_event_handler_nodes_t * handlers)398 static void handler_instances_remove_all(esp_event_handler_nodes_t* handlers)
399 {
400 esp_event_handler_node_t *it, *temp;
401 SLIST_FOREACH_SAFE(it, handlers, next, temp) {
402 SLIST_REMOVE(handlers, it, esp_event_handler_node, next);
403 free(it->handler_ctx);
404 free(it);
405 }
406 }
407
base_node_remove_all_handler(esp_event_base_node_t * base_node)408 static void base_node_remove_all_handler(esp_event_base_node_t* base_node)
409 {
410 handler_instances_remove_all(&(base_node->handlers));
411
412 esp_event_id_node_t *it, *temp;
413 SLIST_FOREACH_SAFE(it, &(base_node->id_nodes), next, temp) {
414 handler_instances_remove_all(&(it->handlers));
415 SLIST_REMOVE(&(base_node->id_nodes), it, esp_event_id_node, next);
416 free(it);
417 }
418 }
419
loop_node_remove_all_handler(esp_event_loop_node_t * loop_node)420 static void loop_node_remove_all_handler(esp_event_loop_node_t* loop_node)
421 {
422 handler_instances_remove_all(&(loop_node->handlers));
423
424 esp_event_base_node_t *it, *temp;
425 SLIST_FOREACH_SAFE(it, &(loop_node->base_nodes), next, temp) {
426 base_node_remove_all_handler(it);
427 SLIST_REMOVE(&(loop_node->base_nodes), it, esp_event_base_node, next);
428 free(it);
429 }
430 }
431
post_instance_delete(esp_event_post_instance_t * post)432 static void inline __attribute__((always_inline)) post_instance_delete(esp_event_post_instance_t* post)
433 {
434 #if CONFIG_ESP_EVENT_POST_FROM_ISR
435 if (post->data_allocated && post->data.ptr) {
436 free(post->data.ptr);
437 }
438 #else
439 if (post->data) {
440 free(post->data);
441 }
442 #endif
443 memset(post, 0, sizeof(*post));
444 }
445
446 /* ---------------------------- Public API --------------------------------- */
447
esp_event_loop_create(const esp_event_loop_args_t * event_loop_args,esp_event_loop_handle_t * event_loop)448 esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, esp_event_loop_handle_t* event_loop)
449 {
450 if (event_loop_args == NULL) {
451 ESP_LOGE(TAG, "event_loop_args was NULL");
452 return ESP_ERR_INVALID_ARG;
453 }
454
455 if (event_loop == NULL) {
456 ESP_LOGE(TAG, "event_loop was NULL");
457 return ESP_ERR_INVALID_ARG;
458 }
459
460 esp_event_loop_instance_t* loop;
461 esp_err_t err = ESP_ERR_NO_MEM; // most likely error
462
463 loop = calloc(1, sizeof(*loop));
464 if (loop == NULL) {
465 ESP_LOGE(TAG, "alloc for event loop failed");
466 return err;
467 }
468
469 loop->queue = xQueueCreate(event_loop_args->queue_size , sizeof(esp_event_post_instance_t));
470 if (loop->queue == NULL) {
471 ESP_LOGE(TAG, "create event loop queue failed");
472 goto on_err;
473 }
474
475 loop->mutex = xSemaphoreCreateRecursiveMutex();
476 if (loop->mutex == NULL) {
477 ESP_LOGE(TAG, "create event loop mutex failed");
478 goto on_err;
479 }
480
481 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
482 loop->profiling_mutex = xSemaphoreCreateMutex();
483 if (loop->profiling_mutex == NULL) {
484 ESP_LOGE(TAG, "create event loop profiling mutex failed");
485 goto on_err;
486 }
487 #endif
488
489 SLIST_INIT(&(loop->loop_nodes));
490
491 // Create the loop task if requested
492 if (event_loop_args->task_name != NULL) {
493 BaseType_t task_created = xTaskCreatePinnedToCore(esp_event_loop_run_task, event_loop_args->task_name,
494 event_loop_args->task_stack_size, (void*) loop,
495 event_loop_args->task_priority, &(loop->task), event_loop_args->task_core_id);
496
497 if (task_created != pdPASS) {
498 ESP_LOGE(TAG, "create task for loop failed");
499 err = ESP_FAIL;
500 goto on_err;
501 }
502
503 loop->name = event_loop_args->task_name;
504
505 ESP_LOGD(TAG, "created task for loop %p", loop);
506 } else {
507 loop->name = "";
508 loop->task = NULL;
509 }
510
511 loop->running_task = NULL;
512
513 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
514 portENTER_CRITICAL(&s_event_loops_spinlock);
515 SLIST_INSERT_HEAD(&s_event_loops, loop, next);
516 portEXIT_CRITICAL(&s_event_loops_spinlock);
517 #endif
518
519 *event_loop = (esp_event_loop_handle_t) loop;
520
521 ESP_LOGD(TAG, "created event loop %p", loop);
522
523 return ESP_OK;
524
525 on_err:
526 if (loop->queue != NULL) {
527 vQueueDelete(loop->queue);
528 }
529
530 if (loop->mutex != NULL) {
531 vSemaphoreDelete(loop->mutex);
532 }
533
534 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
535 if (loop->profiling_mutex != NULL) {
536 vSemaphoreDelete(loop->profiling_mutex);
537 }
538 #endif
539
540 free(loop);
541
542 return err;
543 }
544
545 // On event lookup performance: The library implements the event list as a linked list, which results to O(n)
546 // lookup time. The test comparing this implementation to the O(lg n) performance of rbtrees
547 // (https://github.com/freebsd/freebsd/blob/master/sys/sys/tree.h)
548 // indicate that the difference is not that substantial, especially considering the additional
549 // pointers per node of rbtrees. Code for the rbtree implementation of the event loop library is archived
550 // in feature/esp_event_loop_library_rbtrees if needed.
esp_event_loop_run(esp_event_loop_handle_t event_loop,TickType_t ticks_to_run)551 esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t ticks_to_run)
552 {
553 assert(event_loop);
554
555 esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
556 esp_event_post_instance_t post;
557 TickType_t marker = xTaskGetTickCount();
558 TickType_t end = 0;
559
560 #if (configUSE_16_BIT_TICKS == 1)
561 int32_t remaining_ticks = ticks_to_run;
562 #else
563 int64_t remaining_ticks = ticks_to_run;
564 #endif
565
566 while(xQueueReceive(loop->queue, &post, ticks_to_run) == pdTRUE) {
567 // The event has already been unqueued, so ensure it gets executed.
568 xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
569
570 loop->running_task = xTaskGetCurrentTaskHandle();
571
572 bool exec = false;
573
574 esp_event_handler_node_t *handler, *temp_handler;
575 esp_event_loop_node_t *loop_node, *temp_node;
576 esp_event_base_node_t *base_node, *temp_base;
577 esp_event_id_node_t *id_node, *temp_id_node;
578
579 SLIST_FOREACH_SAFE(loop_node, &(loop->loop_nodes), next, temp_node) {
580 // Execute loop level handlers
581 SLIST_FOREACH_SAFE(handler, &(loop_node->handlers), next, temp_handler) {
582 handler_execute(loop, handler, post);
583 exec |= true;
584 }
585
586 SLIST_FOREACH_SAFE(base_node, &(loop_node->base_nodes), next, temp_base) {
587 if (base_node->base == post.base) {
588 // Execute base level handlers
589 SLIST_FOREACH_SAFE(handler, &(base_node->handlers), next, temp_handler) {
590 handler_execute(loop, handler, post);
591 exec |= true;
592 }
593
594 SLIST_FOREACH_SAFE(id_node, &(base_node->id_nodes), next, temp_id_node) {
595 if (id_node->id == post.id) {
596 // Execute id level handlers
597 SLIST_FOREACH_SAFE(handler, &(id_node->handlers), next, temp_handler) {
598 handler_execute(loop, handler, post);
599 exec |= true;
600 }
601 // Skip to next base node
602 break;
603 }
604 }
605 }
606 }
607 }
608
609 esp_event_base_t base = post.base;
610 int32_t id = post.id;
611
612 post_instance_delete(&post);
613
614 if (ticks_to_run != portMAX_DELAY) {
615 end = xTaskGetTickCount();
616 remaining_ticks -= end - marker;
617 // If the ticks to run expired, return to the caller
618 if (remaining_ticks <= 0) {
619 xSemaphoreGiveRecursive(loop->mutex);
620 break;
621 } else {
622 marker = end;
623 }
624 }
625
626 loop->running_task = NULL;
627
628 xSemaphoreGiveRecursive(loop->mutex);
629
630 if (!exec) {
631 // No handlers were registered, not even loop/base level handlers
632 ESP_LOGD(TAG, "no handlers have been registered for event %s:%d posted to loop %p", base, id, event_loop);
633 }
634 }
635
636 return ESP_OK;
637 }
638
esp_event_loop_delete(esp_event_loop_handle_t event_loop)639 esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
640 {
641 assert(event_loop);
642
643 esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
644 SemaphoreHandle_t loop_mutex = loop->mutex;
645 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
646 SemaphoreHandle_t loop_profiling_mutex = loop->profiling_mutex;
647 #endif
648
649 xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
650
651 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
652 xSemaphoreTake(loop->profiling_mutex, portMAX_DELAY);
653 portENTER_CRITICAL(&s_event_loops_spinlock);
654 SLIST_REMOVE(&s_event_loops, loop, esp_event_loop_instance, next);
655 portEXIT_CRITICAL(&s_event_loops_spinlock);
656 #endif
657
658 // Delete the task if it was created
659 if (loop->task != NULL) {
660 vTaskDelete(loop->task);
661 }
662
663 // Remove all registered events and handlers in the loop
664 esp_event_loop_node_t *it, *temp;
665 SLIST_FOREACH_SAFE(it, &(loop->loop_nodes), next, temp) {
666 loop_node_remove_all_handler(it);
667 SLIST_REMOVE(&(loop->loop_nodes), it, esp_event_loop_node, next);
668 free(it);
669 }
670
671 // Drop existing posts on the queue
672 esp_event_post_instance_t post;
673 while(xQueueReceive(loop->queue, &post, 0) == pdTRUE) {
674 post_instance_delete(&post);
675 }
676
677 // Cleanup loop
678 vQueueDelete(loop->queue);
679 free(loop);
680 // Free loop mutex before deleting
681 xSemaphoreGiveRecursive(loop_mutex);
682 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
683 xSemaphoreGive(loop_profiling_mutex);
684 vSemaphoreDelete(loop_profiling_mutex);
685 #endif
686 vSemaphoreDelete(loop_mutex);
687
688 ESP_LOGD(TAG, "deleted loop %p", (void*) event_loop);
689
690 return ESP_OK;
691 }
692
esp_event_handler_register_with_internal(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,esp_event_handler_instance_context_t ** handler_ctx_arg,bool legacy)693 esp_err_t esp_event_handler_register_with_internal(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
694 int32_t event_id, esp_event_handler_t event_handler, void* event_handler_arg,
695 esp_event_handler_instance_context_t** handler_ctx_arg, bool legacy)
696 {
697 assert(event_loop);
698 assert(event_handler);
699
700 if (event_base == ESP_EVENT_ANY_BASE && event_id != ESP_EVENT_ANY_ID) {
701 ESP_LOGE(TAG, "registering to any event base with specific id unsupported");
702 return ESP_ERR_INVALID_ARG;
703 }
704
705 esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
706
707 if (event_base == ESP_EVENT_ANY_BASE) {
708 event_base = esp_event_any_base;
709 }
710
711 esp_err_t err = ESP_OK;
712
713 xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
714
715 esp_event_loop_node_t *loop_node = NULL, *last_loop_node = NULL;
716
717 SLIST_FOREACH(loop_node, &(loop->loop_nodes), next) {
718 last_loop_node = loop_node;
719 }
720
721 bool is_loop_level_handler = (event_base == esp_event_any_base) && (event_id == ESP_EVENT_ANY_ID);
722
723 if (!last_loop_node ||
724 (last_loop_node && !SLIST_EMPTY(&(last_loop_node->base_nodes)) && is_loop_level_handler)) {
725 loop_node = (esp_event_loop_node_t*) calloc(1, sizeof(*loop_node));
726
727 if (!loop_node) {
728 ESP_LOGE(TAG, "alloc for new loop node failed");
729 err = ESP_ERR_NO_MEM;
730 goto on_err;
731 }
732
733 SLIST_INIT(&(loop_node->handlers));
734 SLIST_INIT(&(loop_node->base_nodes));
735
736 err = loop_node_add_handler(loop_node, event_base, event_id, event_handler, event_handler_arg, handler_ctx_arg, legacy);
737
738 if (err == ESP_OK) {
739 if (!last_loop_node) {
740 SLIST_INSERT_HEAD(&(loop->loop_nodes), loop_node, next);
741 }
742 else {
743 SLIST_INSERT_AFTER(last_loop_node, loop_node, next);
744 }
745 } else {
746 free(loop_node);
747 }
748 }
749 else {
750 err = loop_node_add_handler(last_loop_node, event_base, event_id, event_handler, event_handler_arg, handler_ctx_arg, legacy);
751 }
752
753 on_err:
754 xSemaphoreGiveRecursive(loop->mutex);
755 return err;
756 }
757
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)758 esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
759 int32_t event_id, esp_event_handler_t event_handler, void* event_handler_arg)
760 {
761 return esp_event_handler_register_with_internal(event_loop, event_base, event_id, event_handler, event_handler_arg, NULL, true);
762 }
763
esp_event_handler_instance_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,esp_event_handler_instance_t * handler_ctx_arg)764 esp_err_t esp_event_handler_instance_register_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
765 int32_t event_id, esp_event_handler_t event_handler, void* event_handler_arg,
766 esp_event_handler_instance_t* handler_ctx_arg)
767 {
768 return esp_event_handler_register_with_internal(event_loop, event_base, event_id, event_handler, event_handler_arg, (esp_event_handler_instance_context_t**) handler_ctx_arg, false);
769 }
770
esp_event_handler_unregister_with_internal(esp_event_loop_handle_t event_loop,esp_event_base_t event_base,int32_t event_id,esp_event_handler_instance_context_t * handler_ctx,bool legacy)771 esp_err_t esp_event_handler_unregister_with_internal(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
772 int32_t event_id, esp_event_handler_instance_context_t* handler_ctx, bool legacy)
773 {
774 assert(event_loop);
775 assert(handler_ctx);
776
777 if (event_base == ESP_EVENT_ANY_BASE && event_id != ESP_EVENT_ANY_ID) {
778 ESP_LOGE(TAG, "unregistering to any event base with specific id unsupported");
779 return ESP_FAIL;
780 }
781
782 if (event_base == ESP_EVENT_ANY_BASE) {
783 event_base = esp_event_any_base;
784 }
785
786 esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
787
788 xSemaphoreTakeRecursive(loop->mutex, portMAX_DELAY);
789
790 esp_event_loop_node_t *it, *temp;
791
792 SLIST_FOREACH_SAFE(it, &(loop->loop_nodes), next, temp) {
793 esp_err_t res = loop_node_remove_handler(it, event_base, event_id, handler_ctx, legacy);
794
795 if (res == ESP_OK && SLIST_EMPTY(&(it->base_nodes)) && SLIST_EMPTY(&(it->handlers))) {
796 SLIST_REMOVE(&(loop->loop_nodes), it, esp_event_loop_node, next);
797 free(it);
798 break;
799 }
800 }
801
802 xSemaphoreGiveRecursive(loop->mutex);
803
804 return ESP_OK;
805 }
806
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)807 esp_err_t esp_event_handler_unregister_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
808 int32_t event_id, esp_event_handler_t event_handler)
809 {
810 esp_event_handler_instance_context_t local_handler_ctx;
811 local_handler_ctx.handler = event_handler;
812 local_handler_ctx.arg = NULL;
813
814 return esp_event_handler_unregister_with_internal(event_loop, event_base, event_id, &local_handler_ctx, true);
815 }
816
esp_event_handler_instance_unregister_with(esp_event_loop_handle_t event_loop,esp_event_base_t event_base,int32_t event_id,esp_event_handler_instance_t handler_ctx_arg)817 esp_err_t esp_event_handler_instance_unregister_with(esp_event_loop_handle_t event_loop, esp_event_base_t event_base,
818 int32_t event_id, esp_event_handler_instance_t handler_ctx_arg)
819 {
820 if (!handler_ctx_arg) return ESP_ERR_INVALID_ARG;
821
822 return esp_event_handler_unregister_with_internal(event_loop, event_base, event_id, (esp_event_handler_instance_context_t*) handler_ctx_arg, false);
823 }
824
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)825 esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop, esp_event_base_t event_base, int32_t event_id,
826 void* event_data, size_t event_data_size, TickType_t ticks_to_wait)
827 {
828 assert(event_loop);
829
830 if (event_base == ESP_EVENT_ANY_BASE || event_id == ESP_EVENT_ANY_ID) {
831 return ESP_ERR_INVALID_ARG;
832 }
833
834 esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
835
836 esp_event_post_instance_t post;
837 memset((void*)(&post), 0, sizeof(post));
838
839 if (event_data != NULL && event_data_size != 0) {
840 // Make persistent copy of event data on heap.
841 void* event_data_copy = calloc(1, event_data_size);
842
843 if (event_data_copy == NULL) {
844 return ESP_ERR_NO_MEM;
845 }
846
847 memcpy(event_data_copy, event_data, event_data_size);
848 #if CONFIG_ESP_EVENT_POST_FROM_ISR
849 post.data.ptr = event_data_copy;
850 post.data_allocated = true;
851 post.data_set = true;
852 #else
853 post.data = event_data_copy;
854 #endif
855 }
856 post.base = event_base;
857 post.id = event_id;
858
859 BaseType_t result = pdFALSE;
860
861 // Find the task that currently executes the loop. It is safe to query loop->task since it is
862 // not mutated since loop creation. ENSURE THIS REMAINS TRUE.
863 if (loop->task == NULL) {
864 // The loop has no dedicated task. Find out what task is currently running it.
865 result = xSemaphoreTakeRecursive(loop->mutex, ticks_to_wait);
866
867 if (result == pdTRUE) {
868 if (loop->running_task != xTaskGetCurrentTaskHandle()) {
869 xSemaphoreGiveRecursive(loop->mutex);
870 result = xQueueSendToBack(loop->queue, &post, ticks_to_wait);
871 } else {
872 xSemaphoreGiveRecursive(loop->mutex);
873 result = xQueueSendToBack(loop->queue, &post, 0);
874 }
875 }
876 } else {
877 // The loop has a dedicated task.
878 if (loop->task != xTaskGetCurrentTaskHandle()) {
879 result = xQueueSendToBack(loop->queue, &post, ticks_to_wait);
880 } else {
881 result = xQueueSendToBack(loop->queue, &post, 0);
882 }
883 }
884
885 if (result != pdTRUE) {
886 post_instance_delete(&post);
887
888 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
889 atomic_fetch_add(&loop->events_dropped, 1);
890 #endif
891 return ESP_ERR_TIMEOUT;
892 }
893
894 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
895 atomic_fetch_add(&loop->events_recieved, 1);
896 #endif
897
898 return ESP_OK;
899 }
900
901 #if CONFIG_ESP_EVENT_POST_FROM_ISR
esp_event_isr_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,BaseType_t * task_unblocked)902 esp_err_t esp_event_isr_post_to(esp_event_loop_handle_t event_loop, esp_event_base_t event_base, int32_t event_id,
903 void* event_data, size_t event_data_size, BaseType_t* task_unblocked)
904 {
905 assert(event_loop);
906
907 if (event_base == ESP_EVENT_ANY_BASE || event_id == ESP_EVENT_ANY_ID) {
908 return ESP_ERR_INVALID_ARG;
909 }
910
911 esp_event_loop_instance_t* loop = (esp_event_loop_instance_t*) event_loop;
912
913 esp_event_post_instance_t post;
914 memset((void*)(&post), 0, sizeof(post));
915
916 if (event_data_size > sizeof(post.data.val)) {
917 return ESP_ERR_INVALID_ARG;
918 }
919
920 if (event_data != NULL && event_data_size != 0) {
921 memcpy((void*)(&(post.data.val)), event_data, event_data_size);
922 post.data_allocated = false;
923 post.data_set = true;
924 }
925 post.base = event_base;
926 post.id = event_id;
927
928 BaseType_t result = pdFALSE;
929
930 // Post the event from an ISR,
931 result = xQueueSendToBackFromISR(loop->queue, &post, task_unblocked);
932
933 if (result != pdTRUE) {
934 post_instance_delete(&post);
935
936 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
937 atomic_fetch_add(&loop->events_dropped, 1);
938 #endif
939 return ESP_FAIL;
940 }
941
942 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
943 atomic_fetch_add(&loop->events_recieved, 1);
944 #endif
945
946 return ESP_OK;
947 }
948 #endif
949
esp_event_dump(FILE * file)950 esp_err_t esp_event_dump(FILE* file)
951 {
952 #ifdef CONFIG_ESP_EVENT_LOOP_PROFILING
953 assert(file);
954
955 esp_event_loop_instance_t* loop_it;
956 esp_event_loop_node_t *loop_node_it;
957 esp_event_base_node_t* base_node_it;
958 esp_event_id_node_t* id_node_it;
959 esp_event_handler_node_t* handler_it;
960
961 // Allocate memory for printing
962 int sz = esp_event_dump_prepare();
963 char* buf = calloc(sz, sizeof(char));
964 char* dst = buf;
965
966 char id_str_buf[20];
967
968 // Print info to buffer
969 portENTER_CRITICAL(&s_event_loops_spinlock);
970
971 SLIST_FOREACH(loop_it, &s_event_loops, next) {
972 uint32_t events_recieved, events_dropped;
973
974 events_recieved = atomic_load(&loop_it->events_recieved);
975 events_dropped = atomic_load(&loop_it->events_dropped);
976
977 PRINT_DUMP_INFO(dst, sz, LOOP_DUMP_FORMAT, loop_it, loop_it->task != NULL ? loop_it->name : "none" ,
978 events_recieved, events_dropped);
979
980 int sz_bak = sz;
981
982 SLIST_FOREACH(loop_node_it, &(loop_it->loop_nodes), next) {
983 SLIST_FOREACH(handler_it, &(loop_node_it->handlers), next) {
984 PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler_ctx->handler, "ESP_EVENT_ANY_BASE",
985 "ESP_EVENT_ANY_ID", handler_it->invoked, handler_it->time);
986 }
987
988 SLIST_FOREACH(base_node_it, &(loop_node_it->base_nodes), next) {
989 SLIST_FOREACH(handler_it, &(base_node_it->handlers), next) {
990 PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler_ctx->handler, base_node_it->base ,
991 "ESP_EVENT_ANY_ID", handler_it->invoked, handler_it->time);
992 }
993
994 SLIST_FOREACH(id_node_it, &(base_node_it->id_nodes), next) {
995 SLIST_FOREACH(handler_it, &(id_node_it->handlers), next) {
996 memset(id_str_buf, 0, sizeof(id_str_buf));
997 snprintf(id_str_buf, sizeof(id_str_buf), "%d", id_node_it->id);
998
999 PRINT_DUMP_INFO(dst, sz, HANDLER_DUMP_FORMAT, handler_it->handler_ctx->handler, base_node_it->base ,
1000 id_str_buf, handler_it->invoked, handler_it->time);
1001 }
1002 }
1003 }
1004 }
1005
1006 // No handlers registered for this loop
1007 if (sz == sz_bak) {
1008 PRINT_DUMP_INFO(dst, sz, " NO HANDLERS REGISTERED\n");
1009 }
1010 }
1011
1012 portEXIT_CRITICAL(&s_event_loops_spinlock);
1013
1014 // Print the contents of the buffer to the file
1015 fprintf(file, buf);
1016
1017 // Free the allocated buffer
1018 free(buf);
1019 #endif
1020 return ESP_OK;
1021 }
1022