ESP 事件循环库解析
事件循环库介绍
事件循环库(Event Loop Library)是一种用于管理事件驱动程序的库或框架。在事件驱动编程模型中,程序的执行流程是由事件的发生和处理来驱动的,而不是简单的按照顺序逐行执行。事件循环库提供了一种机制,使得程序可以异步地处理事件,从而提高程序的性能和响应速度。
官方介绍: Event Loop Library
库头文件
首先查看头文件,主要有三个文件需要关注:esp_event_internal.h、esp_event.h、sys\queue.h
其中esp_event提供函数API,esp_event_internal提供结构体支持,sys\queue提供链表支持。
API部分直接看官方介绍和注释就可以搞懂,主要关注点是esp_event_internal文件。
/// Event loop
typedef struct esp_event_loop_instance {
const char* name;
QueueHandle_t queue;
TaskHandle_t task;
TaskHandle_t running_task;
SemaphoreHandle_t mutex;
esp_event_loop_nodes_t loop_nodes;
} esp_event_loop_instance_t;
/// Event posted to the event queue
typedef struct esp_event_post_instance {
esp_event_base_t base;
int32_t id;
esp_event_post_data_t data;
} esp_event_post_instance_t;
这是就是API函数中的loop_handle,只是在函数中为esp_event_loop_handle_t,进行了一次替换。
结构体首先是loop的名称,然后是一个队列头,用于事件发送和接收,传输的数据也很简单,struct esp_event_post_instance一个事件基本集合事件ID,以及用户自定义的数据指针data。接下来是两个TaskHandle_t数据,用于系统调度。还有一个互斥量做保护作用。 重头戏就是这个esp_event_loop_nodes_t变量,它包含许多个链表。
//回调函数 结构体
typedef struct esp_event_handler_context {
esp_event_handler_t handler; //回调函数指针
void* arg; //回调函数输入参数
} esp_event_handler_instance_context_t;
/// Event handler
typedef struct esp_event_handler_node {
esp_event_handler_instance_context_t* handler_ctx;
SLIST_ENTRY(esp_event_handler_node) next;
} esp_event_handler_node_t;
typedef SLIST_HEAD(esp_event_handler_instances, esp_event_handler_node) esp_event_handler_nodes_t;
typedef struct esp_event_loop_node {
esp_event_handler_nodes_t handlers;
esp_event_base_nodes_t base_nodes;
SLIST_ENTRY(esp_event_loop_node) next;
} esp_event_loop_node_t;
typedef SLIST_HEAD(esp_event_loop_nodes, esp_event_loop_node) esp_event_loop_nodes_t;
首先是**esp_event_handler_nodes_t **表示一个回调函数单向链表,在loop_node中的添加的回调函数表示事件和ID集都是ANY。
// 特定BASE事件和特定ID事件链表
typedef struct esp_event_id_node {
int32_t id;
esp_event_handler_nodes_t handlers;
SLIST_ENTRY(esp_event_id_node) next; list */
} esp_event_id_node_t;
typedef SLIST_HEAD(esp_event_id_nodes, esp_event_id_node) esp_event_id_nodes_t;
// 特定BASE事件链表,但其ID事件为所有ID集
typedef struct esp_event_base_node {
esp_event_base_t base;
esp_event_handler_nodes_t handlers;
esp_event_id_nodes_t id_nodes;
SLIST_ENTRY(esp_event_base_node) next;
} esp_event_base_node_t;
typedef SLIST_HEAD(esp_event_base_nodes, esp_event_base_node) esp_event_base_nodes_t;
然后是esp_event_base_nodes_t,和前面的loop_node一样,在每个链表中都有回调函数的链表。base中包含esp_event_id_nodes_t链表和注册事件代码base,符合事件且ID为ANY会执行这里面的回调函数。id_node中包含注册事件ID代码,只要base和id都对应才会执行这里面的回调函数。
库C文件
C文件就只要一个esp_event.c,挑几个重要的介绍一下。
//回调函数执行
static void handler_execute(esp_event_loop_instance_t* loop, esp_event_handler_node_t *handler, esp_event_post_instance_t post)
//添加回调函数到链表中
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)
//移除一个回调函数
static esp_err_t handler_instances_remove(esp_event_handler_nodes_t* handlers, esp_event_handler_instance_context_t* handler_ctx, bool legacy)
//创建事件循环组
esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, esp_event_loop_handle_t* event_loop)
//运行事件循环组,接收队列消息,执行对应回调函数
esp_err_t esp_event_loop_run(esp_event_loop_handle_t event_loop, TickType_t ticks_to_run)
//删除事件循环组
esp_err_t esp_event_loop_delete(esp_event_loop_handle_t event_loop)
//添加事件到循环组中
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)
//推送事件到循环组
esp_err_t esp_event_post_to(esp_event_loop_handle_t event_loop,esp_event_base_t event_base,int32_t event_id,const void *event_data,size_t event_data_size,TickType_t ticks_to_wait);
总结
ESP事件组使用用链表加队列的形式完成。在创建事件组时会创建指定大小的消息队列,事件组运行函数一直接收消息队列数据,并按照事件和事件ID执行相对应的回调函数。事件组注册回调函数时按照三种不同的事件响应策列,把回调函数添加到不同的回调函数链表中,推送函数会把事件和事件ID发送到事件组的队列中。