回顧昨天提到, pending 階段處理 polling 得來的事件, 且調用特定的 callback 。
大家對 pending stage 的各種 case 調用的回調可以稍微研究一下, 會發現 2 種 case 。
一種是 cb (一般回調), 一種是 async_cb (非同步回調)。其中我們知道 cb 是直接接著執行, 那 async_cb 是甚麼呢 ?
查看 uv_process_async_wakeup_req , 這是昨天提到的 pending stage 之一
void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
uv_req_t* req) {
assert(handle->type == UV_ASYNC);
assert(req->type == UV_WAKEUP);
handle->async_sent = 0;
if (handle->flags & UV_HANDLE_CLOSING) {
uv_want_endgame(loop, (uv_handle_t*)handle);
} else if (handle->async_cb != NULL) {
handle->async_cb(handle);
}
}
可以看到 handle->async_cb(handle); 我們接下來的任務就是看看 async_cb 在幹啥
https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/deps/uv/src/win/async.c
查看 uv_async_init
,
它的作用是把 callback 設定進 handle 中, 這樣在 polling stage 才有 callback 觸發
int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
uv_req_t* req;
// 把 handle 加入 handle queue
uv__handle_init(loop, (uv_handle_t*) handle, UV_ASYNC);
// 標示任務未完成
handle->async_sent = 0;
// 設定 handle 的 callback
handle->async_cb = async_cb;
req = &handle->async_req;
UV_REQ_INIT(req, UV_WAKEUP);
req->data = handle;
uv__handle_start(handle);
return 0;
}
其中 async_sent = 0; 表示任務未完成
查看 uv_async_send
它的作用是使 main thread 可以得知該任務完成
int uv_async_send(uv_async_t* handle) {
uv_loop_t* loop = handle->loop;
if (handle->type != UV_ASYNC) {
/* Can't set errno because that's not thread-safe. */
return -1;
}
/* The user should make sure never to call uv_async_send to a closing or
* closed handle. */
assert(!(handle->flags & UV_HANDLE_CLOSING));
// atomic 的把 async_sent 設為 1
if (!uv__atomic_exchange_set(&handle->async_sent)) {
POST_COMPLETION_FOR_REQ(loop, &handle->async_req);
}
return 0;
}
uv__atomic_exchange_set(&handle->async_sent) 把 async_sent 設為 1 , 表示任務完成
最後看 uv_async_endgame
這是 uv_run 這個循環的最後一個階段 ( endgame stage ) 中調用的方法, 會檢查 handle 中的 async_sent , 如果該任務已經完成就會把任務移出 handle queue。
void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING &&
!handle->async_sent) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
uv__handle_close(handle);
}
}
我們發現 async_cb 是被 uv_async_init 設定到 handle 物件中的, 這裡的 handle 是被放進 pending queue 的任務
另外, handle 中有一個值 async_sent 會被設為 0 , 當 handle 被 thread pool 完成時會調用 uv_async_send 方法把 async_sent 設為 1 使 main thread 在 endgame stage 藉由調用 uv_async_endgame 得知該非同步任務完成 , 所以調用結束該任務的方法。
上面所用的方法算是非常重要, 因為這正是 Node 中 threadpool 向 main thread 通訊的方法。
聊聊 TP 的創建, 和 TP 運行的方法
明天見 !