今天繼續看看 event loop 的核心循環, uv_run() , 可以查看以下網址
https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/deps/uv/src/win/core.c
int uv_run(uv_loop_t *loop, uv_run_mode mode) {
DWORD timeout;
int r;
int ran_pending;
r = uv__loop_alive(loop);
if (!r)
uv_update_time(loop);
while (r != 0 && loop->stop_flag == 0) {
uv_update_time(loop);
uv__run_timers(loop);
ran_pending = uv_process_reqs(loop);
uv_idle_invoke(loop);
uv_prepare_invoke(loop);
timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
timeout = uv_backend_timeout(loop);
if (pGetQueuedCompletionStatusEx)
uv__poll(loop, timeout);
else
uv__poll_wine(loop, timeout);
/* Run one final update on the provider_idle_time in case uv__poll*
* returned because the timeout expired, but no events were received. This
* call will be ignored if the provider_entry_time was either never set (if
* the timeout == 0) or was already updated b/c an event was received.
*/
uv__metrics_update_idle_time(loop);
uv_check_invoke(loop);
uv_process_endgames(loop);
if (mode == UV_RUN_ONCE) {
/* UV_RUN_ONCE implies forward progress: at least one callback must have
* been invoked when it returns. uv__io_poll() can return without doing
* I/O (meaning: no callbacks) when its timeout expires - which means we
* have pending timers that satisfy the forward progress constraint.
*
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
* the check.
*/
uv__run_timers(loop);
}
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
/* The if statement lets the compiler compile it to a conditional store.
* Avoids dirtying a cache line.
*/
if (loop->stop_flag != 0)
loop->stop_flag = 0;
return r;
}
今天繼續看主要循環, 我們要來看
uv__poll(loop, timeout);
uv__poll_wine(loop, timeout);
這兩句, 但這兩句基本只是 case 的差別, 所以我們僅看 uv__poll 即可
static void uv__poll(uv_loop_t* loop, DWORD timeout) {
BOOL success;
uv_req_t* req;
OVERLAPPED_ENTRY overlappeds[128];
ULONG count;
ULONG i;
int repeat;
uint64_t timeout_time;
uint64_t user_timeout;
int reset_timeout;
// 訂下此階段結束時間, 時間到就要結束, 不能在一個階段待太久
timeout_time = loop->time + timeout;
if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
reset_timeout = 1;
user_timeout = timeout;
timeout = 0;
} else {
reset_timeout = 0;
}
for (repeat = 0; ; repeat++) {
/* Only need to set the provider_entry_time if timeout != 0. The function
* will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
*/
// 期望 timeout 是 0 , 因為此處使用 IOCP , 實際上有一個任務 queue 裡面可以放任務,
// 理想上只要看一眼 queue 裡有沒有任務, 有取出即可。不要等在那。
if (timeout != 0)
uv__metrics_set_provider_entry_time(loop);
// IOCP 取出 C++ 層註冊的事件所觸發的任務
success = pGetQueuedCompletionStatusEx(loop->iocp,
overlappeds,
ARRAY_SIZE(overlappeds),
&count,
timeout,
FALSE);
if (reset_timeout != 0) {
timeout = user_timeout;
reset_timeout = 0;
}
/* Placed here because on success the loop will break whether there is an
* empty package or not, or if GetQueuedCompletionStatus returned early then
* the timeout will be updated and the loop will run again. In either case
* the idle time will need to be updated.
*/
uv__metrics_update_idle_time(loop);
if (success) {
for (i = 0; i < count; i++) {
/* Package was dequeued, but see if it is not a empty package
* meant only to wake us up.
*/
if (overlappeds[i].lpOverlapped) {
// 轉換成可以推進 pending queue 的型態
// overlapped 是微軟家的折疊變數
req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
// 把 IOCP 取出的任務放入 pending queue 中, 這是一個由 libuv 維護的資料結構
uv_insert_pending_req(loop, req);
}
}
/* Some time might have passed waiting for I/O,
* so update the loop time here.
*/
uv_update_time(loop);
} else if (GetLastError() != WAIT_TIMEOUT) {
/* Serious error */
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx");
} else if (timeout > 0) {
/* GetQueuedCompletionStatus can occasionally return a little early.
* Make sure that the desired timeout target time is reached.
*/
uv_update_time(loop);
if (timeout_time > loop->time) {
timeout = (DWORD)(timeout_time - loop->time);
/* The first call to GetQueuedCompletionStatus should return very
* close to the target time and the second should reach it, but
* this is not stated in the documentation. To make sure a busy
* loop cannot happen, the timeout is increased exponentially
* starting on the third round.
*/
timeout += repeat ? (1 << (repeat - 1)) : 0;
continue;
}
}
break;
}
}
以下簡述觸發流程
明天我們來看看 pending queue 中的 IO 任務, 會在哪邊被處理吧 !
明天見 !