# 通知設計書 1-シグナル通知

## 概要

本ドキュメントは、libuvにおけるOSシグナル通知（uv_signal_cb）の設計を記述する。POSIXシグナル（SIGTERM, SIGINT等）の受信をイベントループのコールバック機構に統合し、安全なシグナルハンドリングを実現する通知メカニズムについて定義する。

### 本通知の処理概要

uv_signal_cbは、OSが送信するPOSIXシグナル（SIGTERM, SIGINT, SIGHUP等）をイベントループ上のコールバックとして安全に処理するための通知メカニズムである。シグナルハンドラ内では非同期シグナルセーフな関数しか呼べないという制約があるため、libuvではパイプを介したself-pipe trickパターンを採用し、シグナルの受信をイベントループに通知する。

**業務上の目的・背景**：POSIXシグナルは非同期に発生し、シグナルハンドラ内で安全に呼び出せる関数は極めて限定される。アプリケーションがシグナルを安全にハンドリングするには、シグナルの受信をイベントループのメインスレッドに委譲し、通常の関数呼び出しが可能なコンテキストでコールバックを実行する必要がある。本通知は、この安全なシグナルハンドリングパターンをlibuvのイベント駆動モデルに統合するために存在する。

**通知の送信タイミング**：uv_signal_start()またはuv_signal_start_oneshot()で登録されたシグナル番号に対応するOSシグナルがプロセスに送信された時点で、シグナルハンドラ（uv__signal_handler）がパイプに通知メッセージを書き込み、イベントループのI/Oポーリングフェーズでパイプの読み取り可能イベントが検出され、uv__signal_event()内でコールバックが呼び出される。

**通知の受信者**：uv_signal_start()でシグナル監視を開始したuv_signal_tハンドルに設定されたコールバック関数（uv_signal_cb）が受信者となる。同一シグナル番号に対して複数のハンドルが登録可能であり、その場合は全てのハンドルのコールバックが呼び出される。

**通知内容の概要**：コールバックには、uv_signal_tハンドルへのポインタと受信したシグナル番号（int signum）が渡される。

**期待されるアクション**：受信者は、シグナルに応じた適切な処理を行う。例えばSIGTERMであればグレースフルシャットダウンの開始、SIGHUPであれば設定の再読み込み等。oneshotモードの場合はコールバック実行後に自動的に監視が停止される。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（self-pipe trick経由でイベントループに通知） |
| 優先度 | 高（シグナルI/Oウォッチャーはイベントループの各イテレーションで処理される） |
| リトライ | なし（パイプへの書き込みがEAGAIN/EWOULDBLOCKの場合は通知がドロップされる） |

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

グローバルなシグナルツリー（Red-Blackツリー: uv__signal_tree）に登録されたuv_signal_tハンドルのうち、受信シグナル番号と一致するsignumを持つ全てのハンドルに対して、対応するイベントループのsignal_pipefdにメッセージが書き込まれる。イベントループ側では、パイプから読み取ったメッセージに含まれるハンドルポインタを通じてコールバックが特定される。

## 通知テンプレート

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

| 項目 | 内容 |
|-----|------|
| 関数型 | `typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum)` |
| 定義場所 | `include/uv.h` 414行目 |

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

```
handle: uv_signal_t* - シグナル監視ハンドル
signum: int - 受信したシグナル番号（SIGTERM=15, SIGINT=2 等）
```

### 添付ファイル

該当なし（Cコールバック通知のため添付ファイルの概念なし）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| handle | シグナルハンドルへのポインタ | uv__signal_msg_t.handle | Yes |
| signum | 受信シグナル番号 | uv__signal_msg_t.signum / handle->signum | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| OSシグナル受信 | プロセスへのシグナル配送 | uv_signal_start()で監視が開始されていること | シグナルハンドラからパイプ経由でイベントループに通知 |
| oneshotシグナル受信 | プロセスへのシグナル配送 | uv_signal_start_oneshot()で監視が開始されていること | 1回限りのコールバック実行後、自動的に監視停止 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| signum == 0 | uv_signal_start()はUV_EINVALを返す（signal.c 388行目） |
| ハンドルがクローズ中 | uv__is_closing(handle)がtrueの場合、start不可（signal.c 382行目） |
| パイプフル | write()がEAGAIN/EWOULDBLOCKを返した場合、通知はドロップされる（signal.c 211-212行目） |
| シグナル番号不一致 | uv__signal_event()内でmsg->signum != handle->signumの場合コールバックはスキップ（signal.c 479行目） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[OSシグナル受信] --> B[uv__signal_handler実行]
    B --> C[シグナルロック取得]
    C --> D[RBツリーから該当ハンドル検索]
    D --> E{該当ハンドルあり?}
    E -->|Yes| F[signal_pipefdにメッセージ書込み]
    E -->|No| G[ロック解放して終了]
    F --> H[caught_signalsインクリメント]
    H --> I[次のハンドルへ]
    I --> E
    G --> J[イベントループのI/Oポーリング]
    J --> K[signal_pipefd読み取り可能検出]
    K --> L[uv__signal_event実行]
    L --> M[パイプからメッセージ読み取り]
    M --> N[handle->signal_cb呼び出し]
    N --> O{oneshot?}
    O -->|Yes| P[uv__signal_stop実行]
    O -->|No| Q[dispatched_signalsインクリメント]
    P --> Q
    Q --> R[終了]
```

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

### 参照テーブル一覧

該当なし（データベースを使用しないCライブラリのため）

### 内部データ構造

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| uv__signal_tree（Red-Blackツリー） | 全ループ共有のシグナルハンドル管理 | signum順→oneshotフラグ順→loopポインタ順でソート |
| uv__signal_msg_t | パイプ経由のシグナル通知メッセージ | handle（ポインタ）とsignum（int）を含む |
| signal_pipefd[2] | ループごとのシグナル通知パイプ | [0]=読み取り側、[1]=書き込み側 |

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| UV_EINVAL | signum == 0 でuv_signal_start()呼び出し | エラーコードを返却 |
| シグナル登録失敗 | sigaction()が失敗（無効なシグナル番号等） | UV__ERR(errno)を返却 |
| パイプ書き込み失敗 | write()がEAGAIN/EWOULDBLOCK | シグナル通知がドロップされる（signal.c 211行目のassert） |
| パイプ読み取り失敗 | read()が-1を返す（EAGAIN/EWOULDBLOCK以外） | abort()で異常終了（signal.c 467-468行目） |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（OSのシグナル配送頻度に依存） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。シグナルはプロセスの実行中いつでも発生し得る。

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

- シグナルハンドラはプロセスの全スレッドで共有されるため、グローバルロック（uv__signal_lock_pipefd）によるスレッドセーフ性を確保している
- シグナルハンドラ内では非同期シグナルセーフな関数（read, write等）のみを使用している
- fork()後の子プロセスではuv__signal_global_reinit()によりパイプが再初期化される

## 備考

- oneshotモードではSA_RESETHANDフラグを使用し、シグナルハンドラが1回実行後にデフォルトに戻る
- 同一シグナル番号に対してoneshotハンドラと通常ハンドラが混在する場合、通常ハンドラが優先される（RBツリーのソート順による）
- caught_signalsとdispatched_signalsのカウンタにより、シグナルの受信と配送の追跡が可能

---

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

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

### 推奨読解順序

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

まず、シグナル通知で使用されるデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | 414行目: uv_signal_cb型定義、235行目: uv_signal_t前方宣言、1722行目: uv_signal_sの構造体定義（signal_cb, signum, caught_signals, dispatched_signalsメンバ） |
| 1-2 | signal.c | `src/unix/signal.c` | 35-38行目: uv__signal_msg_t構造体（パイプ経由メッセージ）、40行目: uv__signal_tree_s（RBツリーヘッド）、54-56行目: グローバル変数群 |

**読解のコツ**: RB_HEAD, RB_GENERATE_STATIC等はFreeBSD由来のRed-Blackツリーマクロ。tree_entryメンバを通じてuv_signal_tノードがツリーに格納される。

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

処理の起点となるAPI関数を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | signal.c | `src/unix/signal.c` | 341-354行目: uv_signal_init() - ハンドル初期化とループのシグナルパイプ初期化 |
| 2-2 | signal.c | `src/unix/signal.c` | 362-371行目: uv_signal_start() / uv_signal_start_oneshot() - 監視開始の公開API |
| 2-3 | signal.c | `src/unix/signal.c` | 374-435行目: uv__signal_start() - 実際の監視開始処理 |

**主要処理フロー**:
1. **388行目**: signum == 0チェック
2. **396行目**: 同一signumの短絡処理（コールバック更新のみ）
3. **406行目**: シグナルマスクブロック＆ロック取得
4. **412-421行目**: 初回登録時のsigaction()によるハンドラ登録
5. **427行目**: RBツリーへのハンドル挿入
6. **431行目**: コールバック設定
7. **432行目**: ハンドルのアクティブ化

#### Step 3: シグナルハンドラ（OSレベル）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | signal.c | `src/unix/signal.c` | 182-220行目: uv__signal_handler() - OSのシグナルハンドラ。パイプへのメッセージ書き込み |
| 3-2 | signal.c | `src/unix/signal.c` | 223-241行目: uv__signal_register_handler() - sigaction()によるハンドラ登録 |

**主要処理フロー**:
- **190行目**: シグナルロック取得
- **195-197行目**: RBツリーから該当signumのハンドル群を走査
- **208行目**: signal_pipefd[1]にメッセージ（uv__signal_msg_t）を書き込み
- **215行目**: caught_signalsカウンタインクリメント

#### Step 4: イベントループでのコールバック配送を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | signal.c | `src/unix/signal.c` | 438-500行目: uv__signal_event() - イベントループ側のコールバック実行 |
| 4-2 | signal.c | `src/unix/signal.c` | 260-283行目: uv__signal_loop_once_init() - ループごとのパイプ＆I/Oウォッチャー初期化 |

**主要処理フロー**:
- **449行目**: signal_pipefd[0]からメッセージ読み取り
- **476-477行目**: メッセージからハンドルポインタ取得
- **481行目**: handle->signal_cb(handle, handle->signum)呼び出し
- **484行目**: dispatched_signalsカウンタインクリメント
- **486-487行目**: oneshotの場合はuv__signal_stop()で自動停止

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

```
uv_signal_init()
    |
    +-- uv__signal_loop_once_init()
    |       +-- uv__make_pipe()
    |       +-- uv__io_init_start()  [UV__SIGNAL_EVENT]
    |
uv_signal_start() / uv_signal_start_oneshot()
    |
    +-- uv__signal_start()
            +-- uv__signal_block_and_lock()
            +-- uv__signal_first_handle()
            +-- uv__signal_register_handler()
            |       +-- sigaction()
            +-- RB_INSERT()
            +-- uv__signal_unlock_and_unblock()
            +-- uv__handle_start()

[OSシグナル受信時]
uv__signal_handler()           ← OSから呼び出し
    +-- uv__signal_lock()
    +-- uv__signal_first_handle()
    +-- write(signal_pipefd[1])  ← パイプにメッセージ送信
    +-- uv__signal_unlock()

[イベントループのI/Oポーリング後]
uv__signal_event()             ← I/Oウォッチャーから呼び出し
    +-- read(signal_pipefd[0])  ← パイプからメッセージ受信
    +-- handle->signal_cb()     ← ユーザーコールバック実行
    +-- uv__signal_stop()       ← oneshot時のみ
```

### データフロー図

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

OSシグナル          uv__signal_handler()
(kill, Ctrl+C等)  ──▶  シグナルハンドラ
                         │
                         ▼
                  write(signal_pipefd[1])
                  uv__signal_msg_t{handle, signum}
                         │
                         ▼
                  [イベントループ I/Oポーリング]
                  read(signal_pipefd[0])
                         │
                         ▼
                  uv__signal_event()
                         │
                         ▼
                  handle->signal_cb(handle, signum)  ──▶ ユーザーコールバック
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_signal_cb型定義、uv_signal_t構造体定義、公開API宣言 |
| signal.c | `src/unix/signal.c` | ソース | Unix向けシグナル通知の全実装（初期化、監視開始/停止、ハンドラ、コールバック配送） |
| internal.h | `src/unix/internal.h` | ヘッダ | 内部関数宣言（uv__signal_global_once_init, uv__signal_event等） |
| uv/unix.h | `include/uv/unix.h` | ヘッダ | Unix固有のuv_signal_t構造体メンバ定義 |
| core.c | `src/unix/core.c` | ソース | イベントループのメインループ（uv__signal_eventの呼び出し元） |
