Skip to main content

libuv 句柄解读-检查(check)

check handle 可以被译为检查句柄,如果程序中启动了 check handle 后,在事件循环中,每当 I/O 循环退出阻塞的时候都会被执行一遍,它与 prepare 刚好是相互呼应的,prepare 是在 I/O 循环进入阻塞前,check 是在 I/O 循环退出阻塞后被调用。

解读

check handle可以被译为检查句柄,如果程序中启动了check handle后,在事件循环中,每当 I/O 循环退出阻塞的时候都会被执行一遍,它与 prepare 刚好是相互呼应的,prepare 是在 I/O 循环进入阻塞前,check 是在 I/O 循环退出阻塞后被调用。

这种设计机制应该是 libuv 留个用户的接口,在退出 I/O 循环的阻塞状态后执行用户自定义的校验操作。

回顾一下 libuv 的事件循环过程,它有一个uv__run_check()函数会被执行,就是在事件循环迭代的过程中处理 check handle。

数据类型

uv_check_t 是 check handle 的数据类型,通过它可以定义一个 check handle 的实例。

typedef struct uv_check_s uv_check_t;

check handle的回调函数

typedef void (*uv_check_cb)(uv_check_t* handle);

如果 check handle 的实例想要执行回调函数,则需要传递一个uv_check_cb类型的回调函数到uv_check_start()函数中。

API

  • 初始化句柄。
int uv_check_init(uv_loop_t* loop, uv_check_t* check)
  • 以给定的回调函数开始句柄。
int uv_check_start(uv_check_t* check, uv_check_cb cb)
  • 停止句柄,回调函数将不会再被调用。
int uv_check_stop(uv_check_t* check)

样例解读

说了那么多,首先方个 check handle 的例子吧,通过例子去讲解 check handle 相关的知识。

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

int64_t num = 0;

void my_idle_cb(uv_idle_t* handle)
{
num++;
printf("idle callback\n");
if (num >= 5) {
printf("idle stop, num = %ld\n", num);
uv_stop(uv_default_loop());
}
}

void my_check_cb(uv_check_t *handle)
{
printf("check callback\n");
}

int main()
{
uv_idle_t idler;
uv_check_t check;

uv_idle_init(uv_default_loop(), &idler);
uv_idle_start(&idler, my_idle_cb);

uv_check_init(uv_default_loop(), &check);
uv_check_start(&check, my_check_cb);

uv_run(uv_default_loop(), UV_RUN_DEFAULT);

return 0;
}

main函数的处理过程:

  • 定义 idler 实例。
  • 定义 check 实例。
  • 初始化 idler 实例。
  • 初始化 check 实例。
  • 启动 idler 实例,并传入对应的回调函数my_idler_cb
  • 启动 check 实例,并传入对应的回调函数my_check_cb
  • 启动事件循环。
  • 在结束后退出。

my_check_cb回调函数的处理:

  • `打印相关的信息

my_idle_cb 回调函数的处理:

  • 在每次调用回调函数的时候,对全局变量计数。
  • 在计数值达到5后,停止事件循环uv_stop()

其实你如果直接全局搜索 uv_check_init 这个函数的话,是找不到它的,因为 libuv 做了很骚的操作,将 check、prepare 以及 check 相关的函数都通过 C 语言的##连接符统一用宏定义了,并且在编译器预处理的时候产生对应的函数代码,具体源码如下:

src\unix\loop-watcher.c文件内容

#include "uv.h"
#include "internal.h"

#define UV_LOOP_WATCHER_DEFINE(name, type) \
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \
handle->name##_cb = NULL; \
return 0; \
} \
\
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
if (uv__is_active(handle)) return 0; \
if (cb == NULL) return UV_EINVAL; \
QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \
handle->name##_cb = cb; \
uv__handle_start(handle); \
return 0; \
} \
\
int uv_##name##_stop(uv_##name##_t* handle) { \
if (!uv__is_active(handle)) return 0; \
QUEUE_REMOVE(&handle->queue); \
uv__handle_stop(handle); \
return 0; \
} \
\
void uv__run_##name(uv_loop_t* loop) { \
uv_##name##_t* h; \
QUEUE queue; \
QUEUE* q; \
QUEUE_MOVE(&loop->name##_handles, &queue); \
while (!QUEUE_EMPTY(&queue)) { \
q = QUEUE_HEAD(&queue); \
h = QUEUE_DATA(q, uv_##name##_t, queue); \
QUEUE_REMOVE(q); \
QUEUE_INSERT_TAIL(&loop->name##_handles, q); \
h->name##_cb(h); \
} \
} \
\
void uv__##name##_close(uv_##name##_t* handle) { \
uv_##name##_stop(handle); \
}

UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
UV_LOOP_WATCHER_DEFINE(check, CHECK)
UV_LOOP_WATCHER_DEFINE(idle, IDLE)

它利用宏定义,在预处理阶段拓展成三个不同类型,但是处理逻辑一样的代码。有三种类型,分别是 prepare,check,check。

如果你将代码中的##name或者name##或者##name##替换为check##type替换为IDLE,就可以得到以下的代码:

  • 这就是编译器预处理生成的 check handle 相关的代码:
int uv_check_init(uv_loop_t* loop, uv_check_t* handle) {

uv__handle_init(loop, (uv_handle_t*)handle, UV_IDLE);

handle->check_cb = NULL;
return 0;
}

int uv_check_start(uv_check_t* handle, uv_check_cb cb) {

if (uv__is_active(handle)) return 0;


if (cb == NULL) return UV_EINVAL;


QUEUE_INSERT_HEAD(&handle->loop->check_handles, &handle->queue);


handle->check_cb = cb;


uv__handle_start(handle);
return 0;
}

int uv_check_stop(uv_check_t* handle) {

if (!uv__is_active(handle)) return 0;


QUEUE_REMOVE(&handle->queue);


uv__handle_stop(handle);
return 0;
}


void uv__run_check(uv_loop_t* loop) {
uv_check_t* h;
QUEUE queue;
QUEUE* q;


QUEUE_MOVE(&loop->check_handles, &queue);


while (!QUEUE_EMPTY(&queue)) {


q = QUEUE_HEAD(&queue);


h = QUEUE_DATA(q, uv_check_t, queue);


QUEUE_REMOVE(q);


QUEUE_INSERT_TAIL(&loop->check_handles, q);


h->check_cb(h);
}
}


void uv__check_close(uv_check_t* handle) {
uv_check_stop(handle);
}

参考