# 通知設計書 11-ワーカー完了通知

## 概要

本ドキュメントは、libuvにおけるワーカー完了通知（uv_after_work_cb）の設計仕様を定義する。スレッドプールで実行された処理が完了した際に、メインイベントループスレッドでコールバックを呼び出す仕組みについて記述する。

### 本通知の処理概要

ワーカー完了通知は、スレッドプール上で実行されたバックグラウンド処理（uv_work_cb）の完了をメインイベントループスレッドに伝達するための通知機構である。uv_queue_work() APIを通じてスレッドプールにタスクを投入し、そのタスクの実行完了後にメインループ上でuv_after_work_cbコールバックが呼び出される。

**業務上の目的・背景**：イベント駆動型のシングルスレッドモデルにおいて、CPU集約的な処理やブロッキングI/O操作をメインループをブロックせずに実行する必要がある。スレッドプールでバックグラウンド処理を行い、その結果をメインスレッドに安全に通知することで、アプリケーションの応答性を維持しつつ重い処理を並行実行できる。Node.jsにおけるファイルI/O、DNS解決、暗号処理などがこの仕組みを利用している。

**通知の送信タイミング**：スレッドプール内のワーカースレッドがuv_work_cb関数の実行を完了した時点で、完了した作業要求がイベントループのワークキュー（loop->wq）に挿入される。その後、uv_async_send()によってメインループが起床し、イベントループの次のイテレーションでuv__work_done()が呼ばれ、キューに溜まった完了済み作業のuv_after_work_cbが順次実行される。また、uv_cancel()によってキャンセルされた場合も、statusにUV_ECANCELEDが設定された状態でコールバックが呼び出される。

**通知の受信者**：uv_queue_work()呼び出し時にafter_work_cbパラメータとして登録されたコールバック関数。メインイベントループスレッド上で実行される。NULLを指定した場合はコールバックは呼び出されない。

**通知内容の概要**：コールバックには、作業要求を表すuv_work_t構造体へのポインタと、処理結果を示すstatusコード（正常完了: 0、キャンセル: UV_ECANCELED）が渡される。uv_work_tのdataフィールドを通じて、ユーザ定義のコンテキストデータにアクセス可能である。

**期待されるアクション**：受信者は処理結果に基づいて後続のアプリケーションロジックを実行する。例えば、ファイル読み込み結果の処理、DNS解決結果の利用、計算結果のクライアントへの送信などが想定される。キャンセルされた場合は適切なエラーハンドリングを行う。

## 通知種別

コールバック通知（C言語関数ポインタによるイベント駆動型通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（スレッドプール実行後、メインループで通知） |
| 優先度 | 中（イベントループの通常フェーズで処理） |
| リトライ | なし（コールバックは1回のみ呼び出される） |

### 送信先決定ロジック

uv_queue_work()呼び出し時にafter_work_cbパラメータとして指定されたコールバック関数が送信先となる。after_work_cbがNULLの場合、コールバックは呼び出されず、リクエストの登録解除のみが行われる（src/threadpool.c 372行目）。

## 通知テンプレート

### コールバック通知の場合

| 項目 | 内容 |
|-----|------|
| コールバック型 | `uv_after_work_cb` |
| シグネチャ | `void (*uv_after_work_cb)(uv_work_t* req, int status)` |
| 実行コンテキスト | メインイベントループスレッド |
| 定義場所 | `include/uv.h` 344行目 |

### コールバックパラメータ

```
req: uv_work_t* - 作業要求構造体
  - req->data: void* - ユーザ定義データ
  - req->loop: uv_loop_t* - 所属イベントループ
  - req->work_cb: uv_work_cb - ワーカー関数
  - req->after_work_cb: uv_after_work_cb - 本コールバック

status: int - 処理結果
  - 0: 正常完了
  - UV_ECANCELED: uv_cancel()によるキャンセル
```

### 添付ファイル

該当なし（C言語コールバック通知のため）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| req | 作業要求構造体 | uv_queue_work()で生成 | Yes |
| status | 処理結果コード | ワーカー実行結果 / uv_cancel() | Yes |
| req->data | ユーザ定義コンテキスト | ユーザ設定 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| スレッドプール完了 | ワーカースレッドがwork_cb実行完了 | after_work_cb != NULL | ワーカー関数の正常完了後にコールバックを呼び出す |
| キャンセル | uv_cancel()呼び出し | after_work_cb != NULL かつ作業がキューで待機中 | キャンセル成功時にstatus=UV_ECANCELEDでコールバックを呼び出す |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| after_work_cb == NULL | uv_queue_work()でNULLが指定された場合、コールバックは呼び出されない |
| uv_cancel()失敗 | 作業が既に実行中の場合はUV_EBUSYが返り、キャンセルされない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[uv_queue_work呼び出し] --> B[uv__work_submit: スレッドプールに投入]
    B --> C[ワーカースレッドがwork_cbを実行]
    C --> D[w->work = NULL設定]
    D --> E[loop->wqキューに完了通知を挿入]
    E --> F[uv_async_send: メインループを起床]
    F --> G[メインループのuv__work_done実行]
    G --> H[loop->wqからキューを移動]
    H --> I{after_work_cb != NULL?}
    I -->|Yes| J[uv__queue_done: req登録解除]
    I -->|No| K[req登録解除のみ]
    J --> L[after_work_cb実行]
    L --> M[終了]
    K --> M
```

## データベース参照・更新仕様

### 参照テーブル一覧

本通知はデータベースを使用しない（インメモリのキューとスレッド同期機構で実装）。

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | C言語のキュー構造体で管理 |

### 内部データ構造

#### グローバルワークキュー（wq）

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| wq | 待機中のワーク要求キュー | mutex保護下でアクセス |
| slow_io_pending_wq | 低速I/Oワーク用キュー | mutex保護下でアクセス |

#### ループローカルキュー（loop->wq）

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| loop->wq | 完了済みワーク要求キュー | loop->wq_mutex保護下でアクセス |
| loop->wq_async | 非同期通知用ハンドル | uv_async_sendで使用 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | インメモリ操作のみ |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| UV_EINVAL | work_cbがNULLの場合（uv_queue_work） | 呼び出し元にエラーコードを返す |
| UV_ECANCELED | uv_cancel()でキャンセルされた場合 | after_work_cbのstatusパラメータで通知 |
| UV_EBUSY | 作業が既に実行中でキャンセル不可 | uv_cancel()の戻り値で通知 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回（リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| スレッドプールサイズ | デフォルト4スレッド（UV_THREADPOOL_SIZE環境変数で変更可能） |
| 最大スレッド数 | 1024（MAX_THREADPOOL_SIZE） |
| スレッドスタックサイズ | 8MB |

### 配信時間帯

時間帯の制限なし。イベントループが動作している限り、完了通知は処理される。ただし、低速I/O作業（UV__WORK_SLOW_IO）はスレッドプールの半分（(nthreads+1)/2）までに制限される。

## セキュリティ考慮事項

- スレッドプールのワーカーとメインループ間のデータ受け渡しはmutex（loop->wq_mutex）で保護されている
- uv_async_send()はスレッドセーフであり、任意のスレッドから安全に呼び出し可能
- ワーカー関数（work_cb）内でイベントループのAPIを直接呼び出すことは安全でない（ドキュメントに明記すべき事項）
- uv_cancel()とワーカー実行のデッドロック回避のため、グローバルmutexとループローカルmutexは同時に保持されないよう設計されている（src/threadpool.c 54-56行目のコメント参照）

## 備考

- UV_THREADPOOL_SIZE環境変数でスレッドプールサイズを1〜1024の範囲で設定可能（src/threadpool.c 207行目）
- スレッドプールはプロセス全体で共有され、uv_once()で遅延初期化される（src/threadpool.c 281行目）
- fork()後はスレッドプールが再初期化される（src/threadpool.c 256-260行目）
- uv_queue_work()はUV__WORK_CPU種別でワークを投入する（src/threadpool.c 393行目）
- DNS解決（getaddrinfo, getnameinfo）、ファイルシステム操作、乱数生成もスレッドプールを使用するが、それぞれ独自のコールバック型を持つ
- メトリクス: 完了イベント数はuv__metrics_inc_events()で記録される（src/threadpool.c 350-354行目）

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、ワーカー完了通知に関わるデータ構造の関係を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | 246行目: uv_work_t型定義、343-344行目: uv_work_cb/uv_after_work_cb型定義、1187-1193行目: uv_work_s構造体（UV_REQ_FIELDS, loop, work_cb, after_work_cb, UV_WORK_PRIVATE_FIELDS） |
| 1-2 | threadpool.h | `include/uv/threadpool.h` | 30-35行目: uv__work構造体（work関数ポインタ, done関数ポインタ, loop, wqキューノード） |
| 1-3 | unix.h | `include/uv/unix.h` | 376-377行目: UV_WORK_PRIVATE_FIELDSの定義（struct uv__work work_req） |

**読解のコツ**: uv_work_tはuv_req_tのサブクラスであり、UV_REQ_FIELDSマクロで共通フィールドを持つ。UV_WORK_PRIVATE_FIELDSによりuv__work構造体がuv_work_t内に埋め込まれ、container_of()マクロで相互に変換される。

#### Step 2: エントリーポイントを理解する

処理の起点となるuv_queue_work()関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | threadpool.c | `src/threadpool.c` | 379-396行目: uv_queue_work()関数 - 引数バリデーション、リクエスト初期化、uv__work_submitへの委譲 |

**主要処理フロー**:
1. **383行目**: work_cbがNULLの場合UV_EINVALを返す
2. **386行目**: uv__req_init()でリクエストをループに登録
3. **387-389行目**: loop, work_cb, after_work_cbをreqに保存
4. **390-394行目**: uv__work_submit()でスレッドプールに投入（UV__WORK_CPU種別）

#### Step 3: スレッドプールへの投入処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | threadpool.c | `src/threadpool.c` | 276-286行目: uv__work_submit()関数 - once初期化、ワーク構造体設定、post()呼び出し |
| 3-2 | threadpool.c | `src/threadpool.c` | 143-161行目: post()関数 - ワークキューへの挿入、スレッド起床 |
| 3-3 | threadpool.c | `src/threadpool.c` | 194-252行目: init_threads()関数 - スレッドプール初期化（UV_THREADPOOL_SIZE読取り、スレッド生成） |

**主要処理フロー**:
- **281行目**: uv_once()でスレッドプールの遅延初期化
- **282-284行目**: uv__work構造体にloop, work, doneを設定
- **285行目**: post()でキューに追加
- **157行目**: uv__queue_insert_tail()でワークキューに挿入
- **158-159行目**: アイドルスレッドがあればuv_cond_signal()で起床

#### Step 4: ワーカースレッドの実行と完了通知を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | threadpool.c | `src/threadpool.c` | 57-140行目: worker()関数 - ワーカースレッドのメインループ |
| 4-2 | threadpool.c | `src/threadpool.c` | 359-363行目: uv__queue_work()関数 - uv_work_tのwork_cbを呼び出すラッパー |
| 4-3 | threadpool.c | `src/threadpool.c` | 366-376行目: uv__queue_done()関数 - 完了処理、after_work_cbの呼び出し |

**主要処理フロー**:
- **122-123行目**: キューからワークを取り出し、w->work(w)で実行
- **125-130行目**: 実行完了後、loop->wqに完了通知を挿入し、uv_async_send()でメインループを起床
- **126行目**: w->work = NULLでuv_cancel()に実行完了を通知

#### Step 5: メインループでの完了処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | threadpool.c | `src/threadpool.c` | 319-356行目: uv__work_done()関数 - 完了キューの処理、コールバック呼び出し |
| 5-2 | threadpool.c | `src/threadpool.c` | 399-429行目: uv_cancel()関数 - 実行前タスクのキャンセル |

**主要処理フロー**:
- **327行目**: container_of()でuv_async_tからuv_loop_tを取得
- **328-330行目**: mutex保護下でloop->wqからローカルキューに移動
- **334-342行目**: キュー内の全完了ワークについてw->done(w, err)を呼び出し
- **339行目**: キャンセル判定（w->work == uv__cancelled）

### プログラム呼び出し階層図

```
uv_queue_work()                       [src/threadpool.c:379]
    |
    +-- uv__req_init()                [マクロ: リクエスト登録]
    +-- uv__work_submit()            [src/threadpool.c:276]
           |
           +-- uv_once(init_once)     [src/threadpool.c:263 - 遅延初期化]
           |      +-- init_threads()  [src/threadpool.c:194 - スレッド生成]
           |
           +-- post()                 [src/threadpool.c:143 - キュー挿入]
                  +-- uv_cond_signal() [スレッド起床]

--- ワーカースレッド側 ---
worker()                              [src/threadpool.c:57]
    |
    +-- uv__queue_work()             [src/threadpool.c:359 - work_cb実行]
    |      +-- req->work_cb(req)
    |
    +-- uv__queue_insert_tail()      [loop->wqに完了挿入]
    +-- uv_async_send()              [メインループ起床]

--- メインループ側 ---
uv__work_done()                       [src/threadpool.c:319]
    |
    +-- uv__queue_move()             [キュー移動]
    +-- uv__queue_done()             [src/threadpool.c:366]
           |
           +-- uv__req_unregister()  [リクエスト登録解除]
           +-- after_work_cb(req, err) [ユーザコールバック実行]
```

### データフロー図

```
[入力]                     [処理]                           [出力]

uv_queue_work()    ──────> uv__work_submit()        ──────> グローバルwqキュー
(work_cb,                  (ワーク構造体設定,
 after_work_cb)             キュー挿入)
                                    |
                                    v
                           worker() [ワーカースレッド]
                           (wqからワーク取得,
                            work_cb実行)
                                    |
                                    v
                           loop->wqキュー           ──────> uv_async_send()
                           (完了通知挿入)                    (メインループ起床)
                                    |
                                    v
                           uv__work_done()          ──────> after_work_cb()
                           (メインループ上で                  (ユーザコールバック)
                            キュー処理)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_work_t構造体、uv_after_work_cb型定義、uv_queue_work/uv_cancel API宣言 |
| threadpool.h | `include/uv/threadpool.h` | ヘッダ | uv__work内部構造体定義 |
| unix.h | `include/uv/unix.h` | ヘッダ | UV_WORK_PRIVATE_FIELDS定義 |
| threadpool.c | `src/threadpool.c` | ソース | スレッドプール実装全体（初期化、ワーカー、投入、完了処理、キャンセル） |
| uv-common.h | `src/uv-common.h` | ヘッダ | uv__work_kind列挙型、uv__work_submit/uv__work_done宣言 |
| uv-common.c | `src/uv-common.c` | ソース | uv_library_shutdown()によるスレッドプールクリーンアップ |
| core.c | `src/unix/core.c` | ソース | uv_run()イベントループ（uv__work_doneが呼ばれるコンテキスト） |
| async.c | `src/unix/async.c` | ソース | uv_async_send()実装（スレッド間通知メカニズム） |
