libuv系列文章
check handle
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)
example
說了那麼多,首先方個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) {
/* 初始化handle的類型,所屬loop,設置UV_HANDLE_REF標誌,並且把handle插入loop->handle_queue隊列的隊尾 */
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) {
/* 如果已經執行過start函數則直接返回 */
if (uv__is_active(handle)) return 0;
/* 回調函數不允許爲空 */
if (cb == NULL) return UV_EINVAL;
/* 把handle插入loop中check_handles隊列,loop有prepare,check和check三個隊列 */
QUEUE_INSERT_HEAD(&handle->loop->check_handles, &handle->queue);
/* 指定回調函數,在事件循環迭代的時候被執行 */
handle->check_cb = cb;
/* 啓動check handle,設置UV_HANDLE_ACTIVE標記並且將loop中的handle的active計數加一,
init的時候只是把handle掛載到loop,start的時候handle才處於激活態 */
uv__handle_start(handle);
return 0;
}
int uv_check_stop(uv_check_t* handle) {
/* 如果check handle沒有被啓動則直接返回 */
if (!uv__is_active(handle)) return 0;
/* 把handle從loop中相應的隊列移除,但是還掛載到handle_queue中 */
QUEUE_REMOVE(&handle->queue);
/* 清除UV_HANDLE_ACTIVE標記並且減去loop中handle的active計數 */
uv__handle_stop(handle);
return 0;
}
/* 在每一輪循環中執行該函數,具體見uv_run */
void uv__run_check(uv_loop_t* loop) {
uv_check_t* h;
QUEUE queue;
QUEUE* q;
/* 把loop的check_handles隊列中所有節點摘下來掛載到queue變量 */
QUEUE_MOVE(&loop->check_handles, &queue);
/* while循環遍歷隊列,執行每個節點裏面的函數 */
while (!QUEUE_EMPTY(&queue)) {
/* 取下當前待處理的節點 */
q = QUEUE_HEAD(&queue);
/* 取得該節點對應的整個結構體的基地址 */
h = QUEUE_DATA(q, uv_check_t, queue);
/* 把該節點移出當前隊列 */
QUEUE_REMOVE(q);
/* 重新插入loop->check_handles隊列 */
QUEUE_INSERT_TAIL(&loop->check_handles, q);
/* 執行對應的回調函數 */
h->check_cb(h);
}
}
/* 關閉這個check handle */
void uv__check_close(uv_check_t* handle) {
uv_check_stop(handle);
}