由来
在我们第一次提交io操作时,会有uv_once被调用,来检测是否初始化过线程池,如果没有则立刻初始化线程池。所以说线程池并非一开始在uv_run的时候或者在loop中初始化的,而是在io操作开始前。
我以uv_open为例子画一下UML图如下:
在uv_open中先初始化req,然后准备提交work,提交前会调用uv_once检测是否初始化线程池,没有则初始化。
init_once
uv_once实现如下:
#define UV_ONCE_INIT PTHREAD_ONCE_INIT
static uv_once_t once = UV_ONCE_INIT;
static void init_once(void) {
#ifndef _WIN32
/* Re-initialize the threadpool after fork.
* Note that this discards the global mutex and condition as well
* as the work queue.
*/
if (pthread_atfork(NULL, NULL, &reset_once))
abort();
#endif
init_threads();
}
在uv__work_submit中uv_once是这样被调用的:
void uv__work_submit(...) {
uv_once(&once, init_once);
...
}
这一部分可以参看TLPI 31.2部分,libuv多做了pthread_atfork的处理。
pthread_atfork注册reset_once函数,在fork之后重置once,保证在libuv循环中如果你fork了一个进程,如果在那个新的进程中你也启动一个libuv,init_threads()能被调用。
init_threads
🐤条件变量
libuv初始化条件变量时,调用自己的uv_cond_init,这个函数只做了一件事情,就是将条件变量的时钟设置为相对时间,这一点是值得我们自己写代码时参考的,相对时间不受系统时间的影响。
int uv_cond_init(uv_cond_t* cond) {
...
err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC