[Day 25] Node Event loop 4

前言

今天继续看看 event loop 的核心循环, uv_run() , 可以查看以下网址

https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/deps/uv/src/win/core.c

主要循环

int uv_run(uv_loop_t *loop, uv_run_mode mode) {
  DWORD timeout;
  int r;
  int ran_pending;

  r = uv__loop_alive(loop);
  if (!r)
    uv_update_time(loop);

  while (r != 0 && loop->stop_flag == 0) {
    uv_update_time(loop);
    uv__run_timers(loop);

    ran_pending = uv_process_reqs(loop);
    uv_idle_invoke(loop);
    uv_prepare_invoke(loop);

    timeout = 0;
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

    if (pGetQueuedCompletionStatusEx)
      uv__poll(loop, timeout);
    else
      uv__poll_wine(loop, timeout);

    /* Run one final update on the provider_idle_time in case uv__poll*
     * returned because the timeout expired, but no events were received. This
     * call will be ignored if the provider_entry_time was either never set (if
     * the timeout == 0) or was already updated b/c an event was received.
     */
    uv__metrics_update_idle_time(loop);

    uv_check_invoke(loop);
    uv_process_endgames(loop);

    if (mode == UV_RUN_ONCE) {
      /* UV_RUN_ONCE implies forward progress: at least one callback must have
       * been invoked when it returns. uv__io_poll() can return without doing
       * I/O (meaning: no callbacks) when its timeout expires - which means we
       * have pending timers that satisfy the forward progress constraint.
       *
       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
       * the check.
       */
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
  }

  /* The if statement lets the compiler compile it to a conditional store.
   * Avoids dirtying a cache line.
   */
  if (loop->stop_flag != 0)
    loop->stop_flag = 0;

  return r;
}

今天继续看主要循环, 我们要来看
ran_pending = uv_process_reqs(loop);

这句代表 pending 阶段

uv_process_reqs

INLINE static int uv_process_reqs(uv_loop_t* loop) {
  uv_req_t* req;
  uv_req_t* first;
  uv_req_t* next;

  if (loop->pending_reqs_tail == NULL)
    return 0;

  first = loop->pending_reqs_tail->next_req;
  next = first;
  loop->pending_reqs_tail = NULL;

  while (next != NULL) {
    req = next;
    next = req->next_req != first ? req->next_req : NULL;

    switch (req->type) {
      case UV_READ:
        DELEGATE_STREAM_REQ(loop, req, read, data);
        break;

      case UV_WRITE:
        DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle);
        break;

      case UV_ACCEPT:
        DELEGATE_STREAM_REQ(loop, req, accept, data);
        break;

      case UV_CONNECT:
        DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle);
        break;

      case UV_SHUTDOWN:
        /* Tcp shutdown requests don't come here. */
        assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE);
        uv_process_pipe_shutdown_req(
            loop,
            (uv_pipe_t*) ((uv_shutdown_t*) req)->handle,
            (uv_shutdown_t*) req);
        break;

      case UV_UDP_RECV:
        uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req);
        break;

      case UV_UDP_SEND:
        uv_process_udp_send_req(loop,
                                ((uv_udp_send_t*) req)->handle,
                                (uv_udp_send_t*) req);
        break;

      case UV_WAKEUP:
        uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
        break;

      case UV_SIGNAL_REQ:
        uv_process_signal_req(loop, (uv_signal_t*) req->data, req);
        break;

      case UV_POLL_REQ:
        uv_process_poll_req(loop, (uv_poll_t*) req->data, req);
        break;

      case UV_PROCESS_EXIT:
        uv_process_proc_exit(loop, (uv_process_t*) req->data);
        break;

      case UV_FS_EVENT_REQ:
        uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data);
        break;

      default:
        assert(0);
    }
  }

  return 1;
}

可以看到里面是标准的 IOCP pattern , 透过检测事件种类, 决定要使用哪种函数

除了定时触发事件, 会在 timer heap (前天内容) 处理, 和其余极少数事件, 其他全在这边处理。

而这里也是 Node 非同步 schedule 的核心实践。

我们随意挑一种事件的处理看看

查看 uv_process_async_wakeup_req

void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
    uv_req_t* req) {
  assert(handle->type == UV_ASYNC);
  assert(req->type == UV_WAKEUP);

  handle->async_sent = 0;

  if (handle->flags & UV_HANDLE_CLOSING) {
    uv_want_endgame(loop, (uv_handle_t*)handle);
  } else if (handle->async_cb != NULL) {
    handle->async_cb(handle);
  }
}

可以看到基本上就是根据事件触发对应的 callback , 而如果还需要後续的 IO 处理, 则会调用 WSAXXX 等等来下达非阻塞的 IO 命令。

整理

我们用一个简化的流程来描述 libuv 的事件循环

  1. libuv 开始循环
  2. timer stage : 检查 time heap 中有没有逾时的定时任务, 有就执行
  3. pending stage : 检查 pending queue 中有没有任务, 有就依照类别执行特定处理与 callback function
  4. polling stage : 利用 IOCP 抓取新发生的事件, 并且存入 pending queue
  5. 回到 timer stage

补充 : 还有一个 endgame queue , pending 做完的事件会被推进该 queue , 在 endgame 阶段关闭。

明天进度

基於篇幅与主题, 我就不去深究其细节, 明天赶紧进入 node 的 thread pool 篇

明天见 !


<<:  Day 10 - 元件的资料传输(2)

>>:  [Day24] Esp32 + LINE - (程序码讲解)

Flutter基础介绍与实作-Day12 Nice to Meet you Widgets-范例实作

前面讲了那麽多关於Basic Widget今天我们就用其中一个来写个小范例吧! 我们用BottomN...

[区块链&DAPP介绍 Day9] Solidity 教学 - control flow

本日来介绍一下 solidity 的控制流程。 学任何语言基本上都需要条件判断式,那就稍微简单介绍一...

Day10 用python写UI-聊聊文字方块Entry

耶~~~终於迈入第十天,完成了三分之一,今天要来讲文字方块,普遍常会看到的用法会在输入号密码的时候,...

Day15 购物车 -- 基础结构

前两天讲得订单不知道大家还有没有印象? 在产生订单之前还有个很常用到的功能----购物车 现代电商没...

30-16 之 DataSource Layer - RowDataGateway

这篇文章我们将说来谈谈《 Patterns of Enterprise Application Ar...