intuv_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);//执行pending队列回调,ran_pending表示pending队列是否为空,即是否没有节点可以执行
ran_pending =uv_process_reqs(loop);//一般来说,所有的io回调(网络,文件,dns)//都会在io轮询阶段执行。但是有的情况下,//io轮询阶段的回调会延迟到下一次循环执行,//那么这种回调就是在pending阶段执行的。//注意,当前版本所有异步io事件统一在这执行。//执行idle队列,遍历双链表执行回调事件uv_idle_invoke(loop);//执行prepare队列,遍历双链表执行回调事件uv_prepare_invoke(loop);
timeout =0;//执行的模式为UV_RUN_ONCE的时候,且当前这次loop循环没有pending队列任务//如果当前这次loop循环的pending队列有任务处理完,那么即使是UV_RUN_ONCE模式,//也不进行阻塞io,因为既然处理了io回调事件,那么响应方可能会再次响应我方//才会触发阻塞式poll io,同时默认模式也是这样if((mode == UV_RUN_ONCE &&!ran_pending)|| mode == UV_RUN_DEFAULT)
timeout =uv_backend_timeout(loop);//计算阻塞超时时间(最长等待时间)//是否一次性处理多个io完成数据包if(pGetQueuedCompletionStatusEx)uv__poll(loop, timeout);//一次性处理多个io完成数据包,性能效率高elseuv__poll_wine(loop, timeout);//一次只处理一个io完成数据包/* 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.
*///如果对io轮询的循环进行额外配置,计算进程空闲时间//防止在多线程的情况下,UV_METRICS_IDLE_TIME状态可能不断更改,未记录到最终的空闲时间uv__metrics_update_idle_time(loop);uv_check_invoke(loop);//执行check队列,遍历双链表执行回调事件uv_process_endgames(loop);//执行close回调队列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);//由于可能是io轮询超时导致io轮询结束,//这是如果loop循环的模式为UV_RUN_ONCE,仍然是有一次执行定时器处理的机会}
r =uv__loop_alive(loop);//判断事件循是否继续执行下去,即检查是否有任务//只执行一次,退出循环,UV_RUN_NOWAIT表示在io轮询阶段不会阻塞并且循环只执行一次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;//返回是否还有活跃的任务(handles或者reqs),业务层可以根据需要选择是否再次执行该loopreturn r;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
uv__hrtime
uint64_tuv__hrtime(unsignedint scale){
LARGE_INTEGER counter;double scaled_freq;double result;assert(hrtime_frequency_ !=0);assert(scale !=0);//检索性能计数器的当前值,这是一个高分辨率 (<1us) 时间戳,可用于时间间隔度量。if(!QueryPerformanceCounter(&counter)){uv_fatal_error(GetLastError(),"QueryPerformanceCounter");}assert(counter.QuadPart !=0);/* Because we have no guarantee about the order of magnitude of the
* performance counter interval, integer math could cause this computation
* to overflow. Therefore we resort to floating point math.
*///因为我们无法保证性能计数器间隔的数量级,整数数学可能会导致此计算溢出。因此,我们采用//浮点数学。
scaled_freq =(double) hrtime_frequency_ / scale;
result =(double) counter.QuadPart / scaled_freq;return(uint64_t) result;}
voiduv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
uv_req_t* req){long dispatched_signum;assert(handle->type == UV_SIGNAL);assert(req->type == UV_SIGNAL_REQ);
dispatched_signum =InterlockedExchange((volatile LONG*)&handle->pending_signum,0);assert(dispatched_signum !=0);/* Check if the pending signal equals the signum that we are watching for.
* These can get out of sync when the handler is stopped and restarted while
* the signal_req is pending. *///如果接收到的信号与预期信号是一致的,则执行回调方法if(dispatched_signum == handle->signum)
handle->signal_cb(handle, dispatched_signum);if(handle->flags & UV_SIGNAL_ONE_SHOT)uv_signal_stop(handle);if(handle->flags & UV_HANDLE_CLOSING){/* When it is closing, it must be stopped at this point. */assert(handle->signum ==0);uv_want_endgame(loop,(uv_handle_t*) handle);}
/* Called on main thread after a child process has exited. *///一个子进程退出后,将在主线程中响应voiduv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle){int64_t exit_code;
DWORD status;assert(handle->exit_cb_pending);
handle->exit_cb_pending =0;/* If we're closing, don't call the exit callback. Just schedule a close
* callback now. */if(handle->flags & UV_HANDLE_CLOSING){uv_want_endgame(loop,(uv_handle_t*) handle);//处理销毁方法return;}/* Unregister from process notification. *///从进程通知中注销该子进程if(handle->wait_handle != INVALID_HANDLE_VALUE){UnregisterWait(handle->wait_handle);
handle->wait_handle = INVALID_HANDLE_VALUE;}/* Set the handle to inactive: no callbacks will be made after the exit
* callback. *///将进程设置为非活跃状态:退出回调后不会再进行回调uv__handle_stop(handle);//获取退出代码,检索指定进程的终止状态if(GetExitCodeProcess(handle->process_handle,&status)){
exit_code = status;}else{/* Unable to obtain the exit code. This should never happen. */
exit_code =uv_translate_sys_error(GetLastError());}/* Fire the exit callback. *///执行退出回调if(handle->exit_cb){
handle->exit_cb(handle, exit_code, handle->exit_signal);}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
uv_process_fs_event_req
voiduv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
uv_fs_event_t* handle){
FILE_NOTIFY_INFORMATION* file_info;int err, sizew, size;char* filename =NULL;
WCHAR* filenamew =NULL;
WCHAR* long_filenamew =NULL;
DWORD offset =0;assert(req->type == UV_FS_EVENT_REQ);assert(handle->req_pending);
handle->req_pending =0;/* Don't report any callbacks if: 在以下情况下不报告任何回调
* - We're closing, just push the handle onto the endgame queue 处于销毁状态
* - We are not active, just ignore the callback 已经处于非活跃状态
*/if(!uv__is_active(handle)){if(handle->flags & UV_HANDLE_CLOSING){uv_want_endgame(loop,(uv_handle_t*) handle);}return;}
file_info =(FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);if(REQ_SUCCESS(req)){if(req->u.io.overlapped.InternalHigh >0){do{
file_info =(FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);assert(!filename);assert(!filenamew);assert(!long_filenamew);/*
* Fire the event only if we were asked to watch a directory,
* or if the filename filter matches.
*///只有当我们被要求监听的目录或者文件名过滤器匹配时,才调用回调事件if(handle->dirw ||file_info_cmp(handle->filew,
file_info->FileName,
file_info->FileNameLength)==0||file_info_cmp(handle->short_filew,
file_info->FileName,
file_info->FileNameLength)==0){if(handle->dirw){/*
* We attempt to resolve the long form of the file name explicitly.
* We only do this for file names that might still exist on disk.
* If this fails, we use the name given by ReadDirectoryChangesW.
* This may be the long form or the 8.3 short name in some cases.
*/if(file_info->Action != FILE_ACTION_REMOVED &&
file_info->Action != FILE_ACTION_RENAMED_OLD_NAME){/* Construct a full path to the file. */
size =wcslen(handle->dirw)+
file_info->FileNameLength /sizeof(WCHAR)+2;
filenamew =(WCHAR*)uv__malloc(size *sizeof(WCHAR));if(!filenamew){uv_fatal_error(ERROR_OUTOFMEMORY,"uv__malloc");}_snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
file_info->FileNameLength /(DWORD)sizeof(WCHAR),
file_info->FileName);
filenamew[size -1]= L'\0';/* Convert to long name. */
size =GetLongPathNameW(filenamew,NULL,0);if(size){
long_filenamew =(WCHAR*)uv__malloc(size *sizeof(WCHAR));if(!long_filenamew){uv_fatal_error(ERROR_OUTOFMEMORY,"uv__malloc");}
size =GetLongPathNameW(filenamew, long_filenamew, size);if(size){
long_filenamew[size]='\0';}else{uv__free(long_filenamew);
long_filenamew =NULL;}}uv__free(filenamew);if(long_filenamew){/* Get the file name out of the long path. */uv_relative_path(long_filenamew,
handle->dirw,&filenamew);uv__free(long_filenamew);
long_filenamew = filenamew;
sizew =-1;}else{/* We couldn't get the long filename, use the one reported. */
filenamew = file_info->FileName;
sizew = file_info->FileNameLength /sizeof(WCHAR);}}else{/*
* Removed or renamed events cannot be resolved to the long form.
* We therefore use the name given by ReadDirectoryChangesW.
* This may be the long form or the 8.3 short name in some cases.
*/
filenamew = file_info->FileName;
sizew = file_info->FileNameLength /sizeof(WCHAR);}}else{/* We already have the long name of the file, so just use it. */
filenamew = handle->filew;
sizew =-1;}/* Convert the filename to utf8. */uv__convert_utf16_to_utf8(filenamew, sizew,&filename);switch(file_info->Action){case FILE_ACTION_ADDED:case FILE_ACTION_REMOVED:case FILE_ACTION_RENAMED_OLD_NAME:case FILE_ACTION_RENAMED_NEW_NAME:
handle->cb(handle, filename, UV_RENAME,0);break;case FILE_ACTION_MODIFIED:
handle->cb(handle, filename, UV_CHANGE,0);break;}uv__free(filename);
filename =NULL;uv__free(long_filenamew);
long_filenamew =NULL;
filenamew =NULL;}
offset = file_info->NextEntryOffset;}while(offset &&!(handle->flags & UV_HANDLE_CLOSING));}else{
handle->cb(handle,NULL, UV_CHANGE,0);}}else{
err =GET_REQ_ERROR(req);
handle->cb(handle,NULL,0,uv_translate_sys_error(err));}if(!(handle->flags & UV_HANDLE_CLOSING)){uv_fs_event_queue_readdirchanges(loop, handle);}else{uv_want_endgame(loop,(uv_handle_t*)handle);}}
staticvoiduv__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;//当前时间 + 超时时间,即具体退出时间//检查是否对io轮询的循环进行额外配置,通过在uv_run之前调用uv_loop_configure方法产生if(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME){
reset_timeout =1;
user_timeout = timeout;
timeout =0;}else{
reset_timeout =0;//通常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.
*///只有当超时时间不为0时需要设置provider_entry_time,如果io轮询没有进行额外配置,如//UV_METRICS_IDLE_TIME,那么uv__metrics_set_provider_entry_time方法将直接returnif(timeout !=0)uv__metrics_set_provider_entry_time(loop);//获取排队队列的完成状态,同时索引多个完整端口状态。
success =pGetQueuedCompletionStatusEx(loop->iocp,//I/O完整端口
overlappeds,//该数组包含GetQueuedCompletionStatusEx函数返回的信息ARRAY_SIZE(overlappeds),//OVERLAPPED_ENTRY数组元素的数目&count,//返回需要处理io事件的个数
timeout,//超时事件
FALSE);//该参数为FALSE,那么意味着是阻塞IO,阻塞时间由超时时间和IO事件决定//如果io轮询的循环设置额外配置,那么io轮询是非阻塞io,同时超时事件重置为上一次需要阻塞的超时时间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.
*///更新空闲时间,前提是io轮询的循环设置额外配置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.
* 数据包已经出队,但需要提前查看它是否只是一个单纯想唤醒我们的空包
* 如果仅仅只是一个唤醒我们的空包,I/O 完成端口不做任何处理
*/if(overlappeds[i].lpOverlapped){//将一个重叠结构转换为uv_req_t对象,利用结构体成员计算出结构体对象首地址//高效数据结构,可以节省一个数据域指针的开销
req =uv_overlapped_to_req(overlappeds[i].lpOverlapped);//插入pending队列单向环形链表尾部uv_insert_pending_req(loop, req);}}/* Some time might have passed waiting for I/O,
* so update the loop time here.
*///等待I/O轮询可能已经过了一段时间,所以请在此处更新循环时间。uv_update_time(loop);}elseif(GetLastError()!= WAIT_TIMEOUT){/* Serious error *///重大错误uv_fatal_error(GetLastError(),"GetQueuedCompletionStatusEx");}elseif(timeout >0){/* GetQueuedCompletionStatus can occasionally return a little early.
* Make sure that the desired timeout target time is reached.
*///GetQueuedCompletionStatus当有IO事件发生时会提前返回//需要保证超时时间达到我们计算所得的超时时间才真正退出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.
*///为了防止GetQueuedCompletionStatus第一次之后返回之后已经非常接近目标时间timeout_time,//然后第二次如果差点达到目标时间,那么按照流程应当调用第三次GetQueuedCompletionStatus//但这实际上只需要将第二次的timeout时间延长一点点,那么就可以不用调用第三次//GetQueuedCompletionStatus IO轮询,所以我们每次timeout + repeat,然后为了//保证高效,所以让repeat成指数增长。//有好处也有坏处,那就是定时器不会实时执行,存在一定的误差,应该repeat是不可控的
timeout += repeat ?(1<<(repeat -1)):0;//0,2^0,2^1,2^2...continue;}}break;}}