# 通知設計書 5-プロセス終了通知

## 概要

本ドキュメントは、libuvにおけるプロセス終了通知（uv_exit_cb）の設計を記述する。子プロセスの終了を検出し、終了ステータスとシグナル番号をコールバックで通知するメカニズムについて定義する。

### 本通知の処理概要

uv_exit_cbは、uv_spawn()で起動した子プロセスが終了した際に、終了ステータス（exit status）と終了シグナル番号（term signal）をコールバックで通知する仕組みである。プラットフォームに応じてSIGCHLDシグナル+waitpid()（Linux等）またはkqueue EVFILT_PROC（macOS/BSD）で子プロセスの終了を検出する。

**業務上の目的・背景**：外部プロセスの実行と管理はサーバーアプリケーションやビルドツールの基本的な要件である。子プロセスの終了を非同期に検出し、終了状態を取得することで、プロセスパイプライン、ジョブスケジューラ、プロセスマネージャ等の構築が可能となる。本通知はイベントループとの統合により、ブロッキングなwait()呼び出しを避けた非同期プロセス管理を実現する。

**通知の送信タイミング**：子プロセスがexit()やシグナルにより終了した時点で、OS側からの通知（SIGCHLDまたはkqueueイベント）を受け、イベントループのイテレーション内でwaitpid()を実行して終了ステータスを取得し、コールバックが呼び出される。

**通知の受信者**：uv_spawn()のoptions.exit_cbで指定されたコールバック関数（uv_exit_cb）が受信者となる。

**通知内容の概要**：コールバックにはuv_process_tハンドル、終了ステータス（int64_t exit_status）、終了シグナル番号（int term_signal）が渡される。正常終了時はexit_statusに終了コード、term_signalは0。シグナルによる終了時はexit_statusは0、term_signalにシグナル番号が設定される。

**期待されるアクション**：受信者は終了状態に応じた処理を行う。例えば、プロセスの再起動、エラーログ出力、パイプラインの次ステップ実行等。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（SIGCHLD/kqueue EVFILT_PROC + waitpid()経由） |
| 優先度 | 高（子プロセスのリソース回収のため速やかに処理される） |
| リトライ | なし |

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

loop->process_handlesキューに登録されたuv_process_tハンドルの中から、waitpid()でpidが一致するプロセスを特定し、そのexit_cbを呼び出す。

## 通知テンプレート

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

| 項目 | 内容 |
|-----|------|
| 関数型 | `typedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal)` |
| 定義場所 | `include/uv.h` |

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

```
process: uv_process_t* - プロセスハンドル
exit_status: int64_t - 終了ステータス（WEXITSTATUS）
term_signal: int - 終了シグナル番号（WTERMSIG、正常終了時は0）
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| process | プロセスハンドル | loop->process_handlesキューから特定 | Yes |
| exit_status | 終了ステータス | WEXITSTATUS(process->status) | Yes |
| term_signal | 終了シグナル番号 | WTERMSIG(process->status) | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SIGCHLDシグナル | 子プロセス終了（Linux等） | uv_spawn()でプロセスが起動済み | uv__chld()→uv__wait_children()で処理 |
| kqueue EVFILT_PROC | 子プロセス終了（macOS/BSD） | uv_spawn()でプロセスが起動済み | NOTE_EXITイベント |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| exit_cb == NULL | コールバック未設定の場合スキップ（process.c 163行目） |
| waitpid失敗（ECHILD） | 他のプロセスがwaitpidを先に実行した場合（process.c 140-145行目） |
| waitpid pid==0 | SIGCHLD使用時、プロセスがまだ終了していない場合（process.c 135-136行目） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[子プロセス終了] --> B{プラットフォーム}
    B -->|Linux等| C[SIGCHLDシグナル]
    B -->|macOS/BSD| D[kqueue EVFILT_PROC NOTE_EXIT]
    C --> E[uv__chld コールバック]
    E --> F[uv__wait_children]
    D --> F
    F --> G[process_handlesキュー走査]
    G --> H[waitpid実行]
    H --> I{pid一致?}
    I -->|Yes| J[pendingキューに移動]
    I -->|No| K[次のハンドルへ]
    K --> G
    J --> L[uv__handle_stop]
    L --> M{exit_cb != NULL?}
    M -->|Yes| N[exit_cb呼び出し]
    M -->|No| O[スキップ]
    N --> P[終了]
    O --> P
```

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

### 内部データ構造

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| loop->process_handles | 実行中プロセスハンドルのキュー | uv__queueリンクリスト |
| process->pid | 子プロセスID | uv_spawn()で設定 |
| process->status | waitpid()の生ステータス | WIFEXITED/WIFSIGNALED等のマクロで解析 |
| process->exit_cb | 終了コールバック | options.exit_cbから設定 |
| loop->child_watcher | SIGCHLD監視用uv_signal_t（Linux等） | UV_HANDLE_INTERNALフラグ付き |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| waitpid ECHILD | 別プロセスがwaitpidを実行済み | 該当プロセスをスキップ（process.c 140-145行目） |
| waitpidエラー（ECHILD以外） | 予期しないエラー | abort()で異常終了（process.c 141行目） |
| uv_spawn失敗 | fork/posix_spawn失敗 | exec_errornoを返却 |
| keventエラー（kqueue使用時） | EVFILT_PROC登録失敗（ESRCH以外） | abort() |
| kevent ESRCH | プロセスが既に終了 | UV_HANDLE_REAPフラグ設定（process.c 1044行目） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | なし |
| リトライ対象エラー | EINTR時のみwaitpid()を再試行 |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし。

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

- UV_PROCESS_SETUID/UV_PROCESS_SETGIDフラグによる権限降格をサポート
- 子プロセスの環境変数（options.env）は親プロセスと分離可能
- cloexec_lockにより、fork中のファイルディスクリプタリークを防止

## 備考

- Linux等ではSIGCHLD + waitpid(WNOHANG)の組み合わせ、macOS/BSDではkqueue EVFILT_PROCを使用
- macOSではposix_spawn()を優先使用し、MAP_JIT問題を回避（process.c 876-892行目）
- process_handlesキューが空になるとSIGCHLD監視を停止（process.c 1121-1122行目）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | uv_exit_cb型定義、uv_process_t構造体、uv_process_options_t構造体 |
| 1-2 | process.c | `src/unix/process.c` | 70-71行目: UV_USE_SIGCHLD条件コンパイル（kqueue有無による分岐） |

**読解のコツ**: UV_USE_SIGCHLDマクロはUV_HAVE_KQUEUEが未定義の場合に定義される。macOS/BSDではkqueue EVFILT_PROCを使用するためUV_USE_SIGCHLDは定義されない。

#### Step 2: プロセス起動（uv_spawn）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | process.c | `src/unix/process.c` | 966-1094行目: uv_spawn() - プロセス起動のメインエントリ |

**主要処理フロー**:
1. **991行目**: uv__handle_init()でハンドル初期化
2. **1018-1020行目**: SIGCHLD監視開始（UV_USE_SIGCHLD時）
3. **1023行目**: uv__spawn_and_init_child()で実際のfork/posix_spawn
4. **1053-1056行目**: 成功時のPID設定、exit_cb設定、process_handlesキュー追加

#### Step 3: 子プロセス終了検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | process.c | `src/unix/process.c` | 75-78行目: uv__chld() - SIGCHLDハンドラ |
| 3-2 | process.c | `src/unix/process.c` | 101-177行目: uv__wait_children() - waitpid()による終了検出とコールバック実行 |

**主要処理フロー**:
- **116-151行目**: process_handlesキュー走査、waitpid()実行、pendingキューへ移動
- **155-175行目**: pendingキューからコールバック実行（exit_status/term_signal計算）
- **167-168行目**: WIFEXITED→WEXITSTATUS
- **171-172行目**: WIFSIGNALED→WTERMSIG

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

```
uv_spawn()
    +-- uv__handle_init()
    +-- uv_signal_start(child_watcher, uv__chld, SIGCHLD)  [UV_USE_SIGCHLD時]
    +-- uv__spawn_and_init_child()
    |       +-- [macOS] uv__spawn_and_init_child_posix_spawn()
    |       |       +-- posix_spawn()
    |       +-- [fallback] uv__spawn_and_init_child_fork()
    |               +-- fork()
    |               +-- uv__process_child_init()
    |                       +-- execvp()
    +-- uv__queue_insert_tail(process_handles)

[子プロセス終了時 - SIGCHLD]
uv__chld()
    +-- uv__wait_children()
            +-- waitpid()
            +-- uv__handle_stop()
            +-- process->exit_cb(process, exit_status, term_signal)

[子プロセス終了時 - kqueue]
kqueue EVFILT_PROC NOTE_EXIT
    +-- uv__wait_children()
```

### データフロー図

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

子プロセス終了       SIGCHLD / kqueue EVFILT_PROC
(exit/signal) ──▶  uv__chld() / uv__wait_children()
                         │
                         ▼
                  waitpid(pid, &status, WNOHANG)
                         │
                         ▼
                  WIFEXITED(status) → exit_status
                  WIFSIGNALED(status) → term_signal
                         │
                         ▼
                  exit_cb(process, exit_status, term_signal) ──▶ ユーザーコールバック
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_exit_cb型定義、uv_process_t/uv_process_options_t構造体、uv_spawn()宣言 |
| process.c | `src/unix/process.c` | ソース | Unix向けプロセス管理の全実装（spawn, wait, kill, close） |
| signal.c | `src/unix/signal.c` | ソース | SIGCHLD監視の基盤（UV_USE_SIGCHLD時） |
| internal.h | `src/unix/internal.h` | ヘッダ | uv__wait_children(), uv__process_init()等の内部関数宣言 |
