libuv源码粗读(4):uv_prepare_t预处理句柄结构体介绍
发布于 9 个月前 作者 xtx1130 743 次浏览 来自 分享

本篇主要是对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 小菜

回到顶部