xiaoxiaojx / blog

Project for records problems solved in my work and study.
https://xiaoxiaojx.github.io/
MIT License
253 stars 6 forks source link

libuv 不常见 api 记录 #41

Open xiaoxiaojx opened 2 years ago

xiaoxiaojx commented 2 years ago

不常见 api 偶尔在某些库中看到有使用, 只能回头看看 uv 代码与文档。隔一阵子又忘记了, 于是决定记录一下 📝

uv_unref

int err = uv_async_init(uv_default_loop(), &(l->async), (uv_async_cb) fuse_native_dispatch); assert(err >= 0);

uv_unref((uv_handle_t *) &(l->async));

在 uv 代码中看到 uv_unref 其实是把当前的活跃句柄给减 1, 活跃句柄的数量是决定事件循环是否继续 uv__loop_alive 判断的条件之一, 所以如果当前任务是事件循环中剩下的最后一个任务时, 则事件循环可以不用考虑该任务, 直接进入退出程序。

为什么少见 uv_ref 的调用, 可以认为 uv_async_init 等操作中已经包含了给活跃句柄加 1 的功能。
```c
// uv 实现

void uv_unref(uv_handle_t* handle) {
  uv__handle_unref(handle);
}

#define uv__handle_unref(h)                                                   \
  do {                                                                        \
    if (((h)->flags & UV_HANDLE_REF) == 0) break;                             \
    (h)->flags &= ~UV_HANDLE_REF;                                             \
    if (((h)->flags & UV_HANDLE_CLOSING) != 0) break;                         \
    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_rm(h);        \
  }                                                                           \
  while (0)

static int uv__loop_alive(const uv_loop_t* loop) {
  return uv__has_active_handles(loop) ||
         uv__has_active_reqs(loop) ||
         loop->closing_handles != NULL;
}

uv_close

// demo

void on_close(uv_handle_t *handle)
{
    delete handle;
}

void cleanup(void* data)
{
    uv_close((uv_handle_t *)data, on_close);
}

void Start(const Napi::CallbackInfo &args)
{
    Napi::Env env = args.Env();
    uv_loop_t *loop;
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    napi_get_uv_event_loop(env, &loop);
    uv_prepare_t* prepare_handle = new uv_prepare_t;
    uv_prepare_init(loop, prepare_handle);
    uv_unref((uv_handle_t *)prepare_handle);
    uv_prepare_start(prepare_handle, [](uv_prepare_t *handle) {});
    node::AddEnvironmentCleanupHook(isolate, cleanup, prepare_handle);
}

可以使用 uvclose 轻易代替 uv##name##close / uv##name##_stop, 通过如下 uv_close 的实现可知

void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { assert(!uv__is_closing(handle));

handle->flags |= UV_HANDLE_CLOSING; handle->close_cb = close_cb;

switch (handle->type) { case UV_NAMED_PIPE: uv__pipe_close((uv_pipe_t*)handle); break;

case UV_TTY: uv__stream_close((uv_stream_t*)handle); break;

case UV_TCP: uv__tcp_close((uv_tcp_t*)handle); break;

case UV_UDP: uv__udp_close((uv_udp_t*)handle); break;

case UV_PREPARE: uv__prepare_close((uv_prepare_t*)handle); break;

case UV_CHECK: uv__check_close((uv_check_t*)handle); break;

case UV_IDLE: uv__idle_close((uv_idle_t*)handle); break;

case UV_ASYNC: uv__async_close((uv_async_t*)handle); break;

case UV_TIMER: uv__timer_close((uv_timer_t*)handle); break;

case UV_PROCESS: uv__process_close((uv_process_t*)handle); break;

case UV_FS_EVENT: uv__fs_event_close((uv_fs_event_t*)handle); break;

case UV_POLL: uv__poll_close((uv_poll_t*)handle); break;

case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t)handle); / Poll handles use file system requests, and one of them may still be

uv_tty_reset_mode

最终是调用 tcgetattr 函数与 tcsetattr 函数控制终端。 如果在某处通过 uv_tty_set_mode 修改了终端参数, 此处用于复原。

// src/node.cc

void ResetStdio() {
  uv_tty_reset_mode();
#ifdef __POSIX__
  for (auto& s : stdio) {
    const int fd = &s - stdio;

    struct stat tmp;
    if (-1 == fstat(fd, &tmp)) {
      CHECK_EQ(errno, EBADF);  // Program closed file descriptor.
      continue;
    }
  }
#endif  // __POSIX__
}

uv_library_shutdown

释放 uv 持有的任何全局状态。 uv 通常会在卸载时自动执行此操作,但可以指示它手动执行清理。调用 uv_library_shutdown() 后不能继续调用 uv 函数