iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
自我挑戰組

從C到JS的同步非同步探索系列 第 26

[Day 26] Node thread pool 1

前言

回顧昨天提到, pending 階段處理 polling 得來的事件, 且調用特定的 callback 。

大家對 pending stage 的各種 case 調用的回調可以稍微研究一下, 會發現 2 種 case 。

一種是 cb (一般回調), 一種是 async_cb (非同步回調)。其中我們知道 cb 是直接接著執行, 那 async_cb 是甚麼呢 ?

導引

查看 uv_process_async_wakeup_req , 這是昨天提到的 pending stage 之一

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);
  }
}

可以看到 handle->async_cb(handle); 我們接下來的任務就是看看 async_cb 在幹啥

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

正文

查看 uv_async_init ,

它的作用是把 callback 設定進 handle 中, 這樣在 polling stage 才有 callback 觸發

int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
  uv_req_t* req;
	// 把 handle 加入 handle queue
  uv__handle_init(loop, (uv_handle_t*) handle, UV_ASYNC);
	// 標示任務未完成
  handle->async_sent = 0;
	// 設定 handle 的 callback
  handle->async_cb = async_cb;

  req = &handle->async_req;
  UV_REQ_INIT(req, UV_WAKEUP);
  req->data = handle;

  uv__handle_start(handle);

  return 0;
}

其中 async_sent = 0; 表示任務未完成

查看 uv_async_send 它的作用是使 main thread 可以得知該任務完成

int uv_async_send(uv_async_t* handle) {
  uv_loop_t* loop = handle->loop;

  if (handle->type != UV_ASYNC) {
    /* Can't set errno because that's not thread-safe. */
    return -1;
  }

  /* The user should make sure never to call uv_async_send to a closing or
   * closed handle. */
  assert(!(handle->flags & UV_HANDLE_CLOSING));
	// atomic 的把 async_sent 設為 1
  if (!uv__atomic_exchange_set(&handle->async_sent)) {
    POST_COMPLETION_FOR_REQ(loop, &handle->async_req);
  }

  return 0;
}

uv__atomic_exchange_set(&handle->async_sent) 把 async_sent 設為 1 , 表示任務完成

最後看 uv_async_endgame

這是 uv_run 這個循環的最後一個階段 ( endgame stage ) 中調用的方法, 會檢查 handle 中的 async_sent , 如果該任務已經完成就會把任務移出 handle queue。

void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
  if (handle->flags & UV_HANDLE_CLOSING &&
      !handle->async_sent) {
    assert(!(handle->flags & UV_HANDLE_CLOSED));
    uv__handle_close(handle);
  }
}

整理

我們發現 async_cb 是被 uv_async_init 設定到 handle 物件中的, 這裡的 handle 是被放進 pending queue 的任務

另外, handle 中有一個值 async_sent 會被設為 0 , 當 handle 被 thread pool 完成時會調用 uv_async_send 方法把 async_sent 設為 1 使 main thread 在 endgame stage 藉由調用 uv_async_endgame 得知該非同步任務完成 , 所以調用結束該任務的方法。

上面所用的方法算是非常重要, 因為這正是 Node 中 threadpool 向 main thread 通訊的方法。

明天進度

聊聊 TP 的創建, 和 TP 運行的方法

明天見 !


上一篇
[Day 25] Node Event loop 4
下一篇
[Day 27] Node thread pool 2
系列文
從C到JS的同步非同步探索30

尚未有邦友留言

立即登入留言