今天继续看看 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;
}
原则上这个循环就是不断的在处理任务, 可以称得上是 node.js 背後默默做事的工具人了。
今天我们先看
uv_update_time(loop);
uv__run_timers(loop);
这两句
void uv_update_time(uv_loop_t* loop) {
uint64_t new_time = uv__hrtime(1000);
assert(new_time >= loop->time);
loop->time = new_time;
}
主要是利用 uv__hrtime 取得新的时间
static void uv__hrtime_init_once(void) {
if (KERN_SUCCESS != mach_timebase_info(&timebase))
abort();
time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");
if (time_func == NULL)
time_func = mach_absolute_time;
}
uint64_t uv__hrtime(uv_clocktype_t type) {
uv_once(&once, uv__hrtime_init_once);
return time_func() * timebase.numer / timebase.denom;
}
uv__hrtime 主要就是调用一些 OS API 取得当下时间
之所以不每次要时间都和 OS 要, 是为了避免 , system call 会阻塞运作, 所以尽量都维持在 user mode
重点环节, 提供注释
void uv__run_timers(uv_loop_t* loop) {
// 取得 heap 首节点
struct heap_node* heap_node;
uv_timer_t* handle;
for (;;) {
// 取得 heap 中最小节点
heap_node = heap_min(timer_heap(loop));
// heap 为空则断开
if (heap_node == NULL)
break;
// 当 最小节点也大於当前时间, 就退出
handle = container_of(heap_node, uv_timer_t, heap_node);
if (handle->timeout > loop->time)
break;
// 删除节点
uv_timer_stop(handle);
// 如果是重复任务, 就再插回 heap
uv_timer_again(handle);
// 执行节点任务
handle->timer_cb(handle);
}
}
这句方法主要是用来处理需要定期触发的任务, 可以看到 node 维护了一个 heap ( 最小树 ) , 当有以时间为触发基准的任务被注册後, 就包成节点放入 heap , 节点中包含事件触发时间及其回调函数。
因为最小树的顶端永远是最小节点 (在这里可以视为触发时间最早者) , 所以可以用最快的速度锁定要处理的任务。 以下简述流程。
明天我们来看看 , uv__io_poll(loop, timeout) ,中间会先跳过一些阶段, 希望藉此可以在看其他阶段前有更宏观的视野。
明天见 !
<<: Day09:09 - User服务(4) - 前端 - JWT token、修改个人资料
确定了offer也确定了报到时间後,距离到职日大概还有两周多的时间,因为自己是北漂青年因此开始寻找後...
更多会员限定文章可以到patreon观看 完整code可以到以下gist Client端HTML &...
看过很多文章提到程序设计师接案的陷阱,因自己非本科出身,所以觉得这些陷阱都不会发生在自己身上,再加上...
前情提要 身後传来了声音:「哈罗,我叫艾草,是你的入门引导学姊。」 我回头一看却没看到人。 「这里!...
前言 今天文章的标题完完全全打脸了笔者在 Day27 的结语,没想到在最後一天仍然还是介绍了早午餐给...