# 通知設計書 2-非同期通知

## 概要

本ドキュメントは、libuvにおけるスレッド間非同期通知（uv_async_cb）の設計を記述する。ワーカースレッドからイベントループスレッドへの安全な通知メカニズムを提供し、マルチスレッドアプリケーションにおけるスレッド間通信の基盤となる通知機構について定義する。

### 本通知の処理概要

uv_async_cbは、任意のスレッドからイベントループスレッドに対して非同期に通知を送る仕組みである。uv_async_send()はスレッドセーフであり、ワーカースレッドから安全に呼び出すことができる。内部ではatomic操作とプラットフォーム固有のウェイクアップメカニズム（Linux: eventfd、macOS: kqueue EVFILT_USER、その他: パイプ）を組み合わせて実装されている。

**業務上の目的・背景**：マルチスレッドアプリケーションでは、ワーカースレッドでの処理完了やイベント発生をメインのイベントループスレッドに通知する必要がある。しかし、イベントループは単一スレッドで動作するため、スレッドセーフな通知メカニズムが必要となる。本通知は、libuvのスレッドプール（uv_queue_work）の完了通知基盤としても使用されており、ライブラリ内部の重要なインフラストラクチャである。

**通知の送信タイミング**：uv_async_send()が呼び出された時点で即座にイベントループのウェイクアップが発生する。ただし、複数回のuv_async_send()呼び出しが1回のコールバック実行に集約される可能性がある（コールバックの回数は保証されない）。

**通知の受信者**：uv_async_init()で登録されたコールバック関数（uv_async_cb）が受信者となる。各uv_async_tハンドルに1つのコールバックが紐づく。

**通知内容の概要**：コールバックにはuv_async_tハンドルへのポインタのみが渡される。通知の詳細なペイロードはなく、「イベントが発生した」というシグナルのみ。追加データの受け渡しにはhandle->dataフィールドやアプリケーション固有のデータ構造を使用する。

**期待されるアクション**：受信者はアプリケーション固有の処理を行う。例えば、ワーカースレッドが準備したデータの読み取り、UIの更新、状態の遷移等。

## 通知種別

コールバック通知（Cコールバック関数呼び出し）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（atomic操作 + eventfd/kqueue/パイプ経由でイベントループをウェイクアップ） |
| 優先度 | 中（I/Oポーリングのウェイクアップで処理される） |
| リトライ | なし（複数回のsendは1回のコールバックに集約可能） |

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

uv_async_send()で指定されたuv_async_tハンドルが属するイベントループのasync_io_watcherを通じてウェイクアップが行われる。イベントループ側ではloop->async_handlesキューに登録された全てのuv_async_tハンドルを走査し、pendingフラグが立っているもののコールバックを実行する。

## 通知テンプレート

### コールバック関数シグネチャ

| 項目 | 内容 |
|-----|------|
| 関数型 | `typedef void (*uv_async_cb)(uv_async_t* handle)` |
| 定義場所 | `include/uv.h` 336行目 |

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

```
handle: uv_async_t* - 非同期通知ハンドル
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| handle | 非同期ハンドルへのポインタ | loop->async_handlesキューから取得 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | uv_async_send()の実行 | uv_async_init()でハンドルが初期化済みであること | 任意のスレッドから呼び出し可能 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| pending == 1（既にセット済み） | atomic_load_explicit(pending)が0以外の場合、早期リターン（async.c 101-102行目） |
| async_cbがNULL | コールバック未設定の場合、uv__async_io内でスキップ（async.c 205-206行目） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[uv_async_send呼び出し] --> B{pending == 0?}
    B -->|No| C[早期リターン]
    B -->|Yes| D[busyフラグインクリメント]
    D --> E[atomic_exchange pending = 1]
    E --> F{以前のpending == 0?}
    F -->|Yes| G[uv__async_send - ループウェイクアップ]
    F -->|No| H[ウェイクアップ不要]
    G --> I[busyフラグデクリメント]
    H --> I
    I --> J[イベントループI/Oポーリング復帰]
    J --> K[uv__async_io実行]
    K --> L[async_handlesキュー走査]
    L --> M{pending != 0?}
    M -->|Yes| N[atomic_exchange pending = 0]
    N --> O[async_cb呼び出し]
    M -->|No| P[次のハンドルへ]
    O --> P
    P --> Q[終了]
    C --> Q
```

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

### 内部データ構造

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| loop->async_handles | 登録済みuv_async_tハンドルのキュー | uv__queueによるリンクリスト |
| handle->pending | 通知保留フラグ（_Atomic int） | 0=保留なし、1=保留あり |
| handle->u.fd | ビジーフラグ（_Atomic int） | uv_async_send中のスレッド数をカウント |
| loop->async_io_watcher | 非同期通知用I/Oウォッチャー | eventfd/パイプの読み取り側を監視 |
| loop->async_wfd | 非同期通知用書き込みfd | パイプの書き込み側（eventfd/EVFILT_USERの場合は-1） |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| eventfd作成失敗 | eventfd()がエラーを返す（Linux） | UV__ERR(errno)を返却 |
| パイプ作成失敗 | uv__make_pipe()がエラーを返す | エラーコードを返却 |
| ウェイクアップ書き込み失敗 | write()がEAGAIN/EWOULDBLOCK以外で失敗 | abort()で異常終了（async.c 254行目） |
| kqueue EVFILT_USER失敗 | kevent()がエラーを返す（macOS） | abort()で異常終了（async.c 239行目） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | なし |
| リトライ対象エラー | EINTR時のみ再試行（writeループ） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。ただし、複数回のuv_async_send()は1回のコールバック実行に集約される可能性がある。

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

- uv_async_send()はスレッドセーフであり、atomic操作で排他制御される
- busyフラグによりuv_close()時のスピンウェイト（uv__async_spin）でスレッドセーフなクローズを実現
- fork()後はpendingとbusyフラグがリセットされ、async_io_watcherが再初期化される（async.c 366-405行目）

## 備考

- プラットフォーム別のウェイクアップメカニズム: Linux=eventfd、macOS=kqueue EVFILT_USER（ランタイム検出あり）、その他=パイプ
- macOSではEVFILT_USERのランタイム検出を行い、非サポート環境ではパイプにフォールバック
- libuvの内部インフラとして、スレッドプール完了通知（loop->wq_async）にも使用される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | 336行目: uv_async_cb型定義、231行目: uv_async_t前方宣言、961-963行目: uv_async_init/uv_async_send公開API |

**読解のコツ**: uv_async_tのpendingフィールドとu.fdフィールドは_Atomic intとして使用される。これはC11のatomic操作で排他制御される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | async.c | `src/unix/async.c` | 74-90行目: uv_async_init() - ハンドル初期化、async_handlesキューへの追加 |
| 2-2 | async.c | `src/unix/async.c` | 93-115行目: uv_async_send() - スレッドセーフな通知送信 |

**主要処理フロー**:
1. **101行目**: pending==0のチェック（cheap read）
2. **105行目**: busyフラグのatomicインクリメント
3. **108行目**: pendingフラグのatomic exchange
4. **109行目**: 以前のpending==0なら実際のウェイクアップ送信

#### Step 3: プラットフォーム別ウェイクアップを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | async.c | `src/unix/async.c` | 213-255行目: uv__async_send() - プラットフォーム別のイベントループウェイクアップ |
| 3-2 | async.c | `src/unix/async.c` | 258-329行目: uv__async_start() - プラットフォーム別の初期化 |

**主要処理フロー**:
- **224-229行目**: Linux eventfd経由の通知
- **231-240行目**: macOS kqueue EVFILT_USER経由の通知
- **243-244行目**: パイプ経由の通知（フォールバック）

#### Step 4: コールバック配送を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | async.c | `src/unix/async.c` | 160-210行目: uv__async_io() - I/Oウォッチャーコールバック、全async_handlesの走査 |

**主要処理フロー**:
- **175行目**: eventfd/パイプからの読み取り（ウェイクアップの消費）
- **192-198行目**: async_handlesキューの走査
- **202行目**: pendingフラグのatomicクリア
- **208行目**: async_cb(h)コールバック実行

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

```
uv_async_init()
    |
    +-- uv__async_start()
    |       +-- eventfd() / uv__make_pipe() / kqueue EVFILT_USER登録
    |       +-- uv__io_init_start()  [UV__ASYNC_IO]
    |
    +-- uv__handle_init()
    +-- uv__handle_start()

[別スレッドから]
uv_async_send()
    +-- atomic_load(pending)     ← cheap read
    +-- atomic_fetch_add(busy)   ← ビジーフラグ
    +-- atomic_exchange(pending)
    +-- uv__async_send()
    |       +-- write(eventfd) / kevent(EVFILT_USER) / write(pipe)
    +-- atomic_fetch_add(busy, -1)

[イベントループのI/Oポーリング後]
uv__async_io()
    +-- read(eventfd/pipe)      ← ウェイクアップの消費
    +-- async_handlesキュー走査
        +-- atomic_exchange(pending, 0)
        +-- h->async_cb(h)     ← ユーザーコールバック実行
```

### データフロー図

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

ワーカースレッド          uv_async_send()
uv_async_send() ──▶  atomic pending=1
                         │
                         ▼
                  uv__async_send()
                  write(eventfd/pipe) or kevent(EVFILT_USER)
                         │
                         ▼
                  [イベントループ I/Oポーリング復帰]
                  uv__async_io()
                         │
                         ▼
                  pendingハンドル走査
                  atomic pending=0
                         │
                         ▼
                  h->async_cb(h) ──▶ ユーザーコールバック
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_async_cb型定義、uv_async_t構造体定義、公開API宣言 |
| async.c | `src/unix/async.c` | ソース | Unix向け非同期通知の全実装（初期化、送信、コールバック配送、クリーンアップ） |
| internal.h | `src/unix/internal.h` | ヘッダ | 内部関数宣言（uv__async_io, uv__async_send等） |
| uv/unix.h | `include/uv/unix.h` | ヘッダ | Unix固有のuv_async_t構造体メンバ定義 |
| threadpool.c | `src/threadpool.c` | ソース | loop->wq_asyncの使用箇所（スレッドプール完了通知） |
