You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
intuv_run(uv_loop_t*loop, uv_run_modemode) {
inttimeout;
intr;
intran_pending;
r=uv__loop_alive(loop); // 判断是否还存在活跃句柄if (!r)
uv__update_time(loop); // 如果不存在直接更新event-loop的loop->time(libuv事件循环内部维护的时间)while (r!=0&&loop->stop_flag==0) {
uv__update_time(loop); // 更新`loop->time`的时间uv__run_timers(loop); // 处理timers相关事件ran_pending=uv__run_pending(loop); // 处理pending相关事件uv__run_idle(loop); // 处理idle相关事件uv__run_prepare(loop); // 处理prepare相关事件timeout=0; // 初始化uv__io_poll的轮询时间timeoutif ((mode==UV_RUN_ONCE&& !ran_pending) ||mode==UV_RUN_DEFAULT) // 添加对evnet-loop运行模式的判断,从而决定uv__io_poll要阻塞的时长timeout=uv_backend_timeout(loop);
uv__io_poll(loop, timeout); // 执行`uv__io_poll`阻塞循环`timeout`时长uv__run_check(loop); // 处理check相关事件uv__run_closing_handles(loop); // 处理close相关事件if (mode==UV_RUN_ONCE) { // 添加对evnet-loop运行模式的判断,从而决定是否再次更新loop->time处理timers相关事件/* 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__update_time(loop);
uv__run_timers(loop);
}
r=uv__loop_alive(loop); // 判断是否还存在活跃句柄if (mode==UV_RUN_ONCE||mode==UV_RUN_NOWAIT) // 添加对evnet-loop运行模式的判断从而决定是否跳出event-loopbreak;
}
/* The if statement lets gcc compile it to a conditional store. Avoids * dirtying a cache line. */if (loop->stop_flag!=0)
loop->stop_flag=0;
returnr;
}
UV_UNUSED(staticvoiduv__update_time(uv_loop_t*loop)) {
/* Use a fast time source if available. We only need millisecond precision. */loop->time=uv__hrtime(UV_CLOCK_FAST) / 1000000;
}
intuv_timer_start(uv_timer_t*handle,
uv_timer_cbcb,
uint64_ttimeout,
uint64_trepeat) {
uint64_tclamped_timeout;
if (cb==NULL)
returnUV_EINVAL;
if (uv__is_active(handle))
uv_timer_stop(handle);
clamped_timeout=handle->loop->time+timeout;
if (clamped_timeout<timeout)
clamped_timeout= (uint64_t) -1;
handle->timer_cb=cb;
handle->timeout=clamped_timeout;
handle->repeat=repeat;
/* start_id is the second index to be compared in uv__timer_cmp() */handle->start_id=handle->loop->timer_counter++;
heap_insert(timer_heap(handle->loop),
(structheap_node*) &handle->heap_node,
timer_less_than);
uv__handle_start(handle);
return0;
}
functionTimeout(callback,after,args,isRepeat){after*=1;// coalesce to number or NaNif(!(after>=1&&after<=TIMEOUT_MAX)){// ...after=1;// schedule on next tick, follows browser behavior}// ...}
libuv event-loop简介
event-loop
相关的代码直接翻到core.c,这里面的逻辑非常清晰。uv_run
是evnet-loop的核心方法,其中设定了事件循环中关键的触发逻辑,通读一下这段代码就能得出来初步的认识,相关注释已经加入到了源码的后面。事件循环中的几个判断
uv_run_mode简介
在介绍这里面的判断之前,先详细介绍一下
uv_run_mode
,其取值有三种,分别为:UV_RUN_DEFAULT
默认轮询模式,此模式会一直运行事件循环直到没有活跃句柄、引用句柄、和请求句柄UV_RUN_ONCE
一次轮询模式,此模式如果pending_queue
中有回调,则会执行回调而直接跨过uv__io_poll
。如果没有,则此方式只会执行一次i/o轮询(uv__io_poll
)。如果在执行过后有回调压入到了pending_queue
中,则uv_run
会返回非0,你需要在未来的某个时间再次触发一次uv_run
来清空pending_queue
。UV_RUN_NOWAIT
一次轮询(无视pending_queue
)模式,此模式类似UV_RUN_ONCE
但是不会判断pending_queue
是否存在回调,直接进行一次i/o轮询。活跃句柄判断
活跃句柄判断代码如下:
这个地方主要是用于判断本次事件循环中是否有活跃句柄,
uv__loop_alive
方法展开如下:这里面做了三类判断,首先是循环结构体(
uv_loop_t
)中是否还存在活跃句柄(loop->active_handles
)和请求句柄(loop->active_reqs.count),其次,对未结束的句柄进行了判断如果存在未结束的句柄会在后面的uv__run_closing_handles(loop)
进行句柄的unref操作,之后会调用handle->close_cb(handle);
来触发执行close事件回调。timeout赋值之前的判断
代码如下:
timeout
变量决定了uv__io_poll
的阻塞时长,如果大家翻看过我之前写过的文章,在node源码粗读(8):setImmediate注册+触发全流程解析的uv_idle简介
章节中,详细介绍了timeout
部分,在这里我就不做过多讲解了。额好吧……在这里再多说一句,
!ran_pending
会进入到判断中是为了验证其余条件是否满足跳过poll阶段,而如果pending_queue
存在的话是可以直接跨过poll阶段的没有必要进入到uv_backend_timeout
中做多余的判断,这里是结合了mode == UV_RUN_ONCE && !ran_pending
所作出的判断。UV_RUN_ONCE模式判断
代码如下:
这个地方是对
UV_RUN_ONCE
追加的保证uv__io_poll
阻塞之后定时器到期所进行的回调。而UV_RUN_NOWAIT
则是单纯的为了进行一次i/o轮询,目的性强不保证进度,因此在检查中省略了它。libuv loop->time 时间计算详解
在代码中,大家可以发现,
uv__update_time
总是伴随着uv__run_timers
出现。下面给大家解释下uv__update_time
:在libuv的
uv_loop_t
结构体中会维护一个time
属性,这个loop->time
则是event-loop中用来执行定时任务的时间计算器,每次调用他都会更新出最新的event-loop时间,这个时间则是和uv_timer_t
息息相关的,uv_timer_t
注册代码如下:通过
clamped_timeout = handle->loop->time + timeout;
这段代码可以发现,uv__run_timers
真正的运行时间是loop->time
(uv_timer_t
句柄注册时的event-loop时间)+timeout
(延迟触发时间)。setTimeout和setImmediate
在nodejs中,如果你输入如下代码:
会发现输出顺序是随机的,接下来给大家详细解释一下这里的随机性,视线首先转移到
setTimeout
的实现原理[internal/timers.js]中:在这里对
setTimeout
的延迟时间做了判定,如果没有设定延迟时间则会默认为1毫秒的延迟触发。继而延伸到libuv,在event-loop的uv__run_timers
中调用handle->timer_cb(handle)
来触发回调。在node源码粗读(8):setImmediate注册+触发全流程解析的setImmediate的执行
章节中,详细介绍了setImmediate
的执行机制,setImmediate
是在uv__run_check
阶段触发。在libuv进行初始化的过程中,如果时间小于1毫秒,则会直接跳过
uv__run_timers
使得uv__run_check
中的回调队列优先触发;而如果初始化时间大于1毫秒,则会进入到uv__run_timers
阶段优先触发setTimeout
中的回调。The text was updated successfully, but these errors were encountered: