Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libuv源码粗读(4):uv_prepare_t预处理句柄结构体介绍 #33

Open
xtx1130 opened this issue Nov 8, 2018 · 1 comment
Open

Comments

@xtx1130
Copy link
Owner

xtx1130 commented Nov 8, 2018

本篇主要是对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)的状态。

by 小菜

@xtx1130
Copy link
Owner Author

xtx1130 commented Nov 9, 2018

最近公司项目改版,忙成🐶了,文章已更完。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant