本篇主要是对
uv_prepare_t
句柄的介绍
uv_prepare_t声明
还是从uv.h切入,便能找到关于uv_prepare_t
的声明:
struct uv_prepare_s {
UV_HANDLE_FIELDS
UV_PREPARE_PRIVATE_FIELDS
};
其中的私有宏UV_PREPARE_PRIVATE_FIELDS
展开如下:
#define UV_PREPARE_PRIVATE_FIELDS \
uv_prepare_cb prepare_cb; \
void* queue[2]; \
uv_prepare_t
可谓是中规中矩了,只有一个回调,以及一个句柄队列指针。而这个句柄为何起名叫prepare,则是因为uv__run_prepare
会在uv__io_poll
阻塞进程之前运行。
prepare 相关api
视线转移到loop-watche.c中:
UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
把这个宏展开如下:
#define UV_LOOP_WATCHER_DEFINE(name, type) \
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \
handle->name##_cb = NULL; \
return 0; \
} \
\
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
if (uv__is_active(handle)) return 0; \
if (cb == NULL) return UV_EINVAL; \
QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \
handle->name##_cb = cb; \
uv__handle_start(handle); \
return 0; \
} \
\
int uv_##name##_stop(uv_##name##_t* handle) { \
if (!uv__is_active(handle)) return 0; \
QUEUE_REMOVE(&handle->queue); \
uv__handle_stop(handle); \
return 0; \
} \
\
void uv__run_##name(uv_loop_t* loop) { \
uv_##name##_t* h; \
QUEUE queue; \
QUEUE* q; \
QUEUE_MOVE(&loop->name##_handles, &queue); \
while (!QUEUE_EMPTY(&queue)) { \
q = QUEUE_HEAD(&queue); \
h = QUEUE_DATA(q, uv_##name##_t, queue); \
QUEUE_REMOVE(q); \
QUEUE_INSERT_TAIL(&loop->name##_handles, q); \
h->name##_cb(h); \
} \
} \
\
void uv__##name##_close(uv_##name##_t* handle) { \
uv_##name##_stop(handle); \
}
可以看出,这个宏模板提供了几个方法,分别为:init、start、stop、close以及run。
- init: 初始化句柄,并设置句柄回调为空
- start: 修改句柄状态为活跃状态,其中
uv__handle_start
宏展开如下:
// node/blob/master/deps/uv/src/uv-common.h
#define uv__handle_start(h) \
do { \
if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \
(h)->flags |= UV_HANDLE_ACTIVE; \ // 设置为活跃句柄
if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h); \ // 活跃句柄计数+1
} \
while (0)
- run: 供事件循环(
uv_run
)调用以触发回调函数运行,在这里需要注意一点:
QUEUE_REMOVE(q); \
QUEUE_INSERT_TAIL(&loop->name##_handles, q); \
在句柄队列执行了QUEUE_REMOVE
之后,队列中的句柄并没有真正被移除,而是又通过QUEUE_INSERT_TAIL
插入到了队尾,意即运行loop-watcher
句柄(uv_prepare_t
/uv_check_t
/uv_idle_t
)并不会清除句柄队列
- stop: 修改句柄状态为停止状态,其中
uv__handle_stop
宏展开如下:
// node/blob/master/deps/uv/src/uv-common.h
#define uv__handle_stop(h) \
do { \
if (((h)->flags & UV_HANDLE_ACTIVE) == 0) break; \
(h)->flags &= ~UV_HANDLE_ACTIVE; \ // 设置为非活跃句柄
if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_rm(h); \ // 活跃句柄计数-1
} \
while (0)
- close:关闭句柄。通过
uv_prepare_stop
实现
loop-watcher 事件循环监视器句柄
通过uv_prepare_t
我们可以引入一个概念:事件循环监视器。这些监视器有一个共性就是:运行完成后不会清除句柄队列。这里的监视器指的是loop-watcher
中定义的句柄:
UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
UV_LOOP_WATCHER_DEFINE(check, CHECK)
UV_LOOP_WATCHER_DEFINE(idle, IDLE)
即:uv_prepare_t
/uv_check_t
/uv_idle_t
。
node中对uv_prepare_t的应用
在env.cc中,可发现如下代码:
void Environment::StartProfilerIdleNotifier() {
if (profiler_idle_notifier_started_)
return;
profiler_idle_notifier_started_ = true;
uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
env->isolate()->SetIdle(true);
});
uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
env->isolate()->SetIdle(false);
});
}
其中的uv_prepare_start
真正唤醒了prepare handle,node通过libuv的prepare句柄注册SetIdle
,下面是SetIdle
的源码:
void Isolate::SetIdle(bool is_idle) {
if (!is_profiling()) return;
StateTag state = current_vm_state();
DCHECK(state == EXTERNAL || state == IDLE);
if (js_entry_sp() != kNullAddress) return;
if (is_idle) {
set_current_vm_state(IDLE);
} else if (state == IDLE) {
set_current_vm_state(EXTERNAL);
}
}
结合上面代码的profiler_idle_notifier_started_
。可以发现,node在event-loop的uv_run_prepare
阶段来通知此时vm(v8)的状态。
原文地址:https://github.com/xtx1130/blog/issues/33, 如果文中有误,还请大神斧正 by 小菜