# 通知設計書 12-ハンドルクローズ通知

## 概要

本ドキュメントは、libuvにおけるハンドルクローズ通知（uv_close_cb）の設計仕様を定義する。全てのハンドル型に共通するクローズ処理が完了した際に、コールバックで通知する仕組みについて記述する。

### 本通知の処理概要

ハンドルクローズ通知は、libuvのハンドル（uv_handle_t及びその全サブクラス）のクローズ処理が完了した際に、メインイベントループスレッドでコールバックを呼び出す通知機構である。uv_close()を呼び出すと、ハンドルは即座にクローズリスト（closing_handles）に追加され、イベントループのクローズフェーズ（uv__run_closing_handles）でコールバックが実行される。

**業務上の目的・背景**：イベント駆動型プログラミングにおいて、ハンドルのライフサイクル管理は極めて重要である。ハンドルは即座に破棄できず、保留中のI/O操作やOSリソースのクリーンアップが必要なため、非同期的にクローズ処理を行う。クローズ完了通知により、ユーザはハンドルに関連するリソース（メモリ、ファイルディスクリプタなど）を安全に解放するタイミングを正確に知ることができる。全13種類のハンドル型（TCP, UDP, Pipe, Timer, Signal, Process, Poll, Prepare, Check, Idle, Async, FS Event, FS Poll）に共通する唯一の統一的なクリーンアップ通知メカニズムである。

**通知の送信タイミング**：uv_close()が呼ばれると、ハンドルにUV_HANDLE_CLOSINGフラグが設定され、ハンドル型固有のクローズ処理が実行された後、uv__make_close_pending()によりクローズリストに追加される。イベントループの各イテレーションの最終段階（uv__run_closing_handles）で、クローズリスト上の全ハンドルに対してuv__finish_close()が呼ばれ、UV_HANDLE_CLOSEDフラグの設定、ハンドルキューからの除去、close_cbの呼び出しが行われる。

**通知の受信者**：uv_close()呼び出し時にclose_cbパラメータとして指定されたコールバック関数。NULLを指定した場合、コールバックは呼び出されないが、クローズ処理自体は正常に完了する。

**通知内容の概要**：コールバックにはクローズされたハンドルへのポインタ（uv_handle_t*）が渡される。この時点でハンドルのUV_HANDLE_CLOSEDフラグは既に設定されており、ハンドルはイベントループのハンドルキューから除去済みである。ユーザはこのコールバック内でハンドルのメモリ解放を安全に行える。

**期待されるアクション**：受信者はハンドルに関連付けられたユーザデータ（handle->data）やハンドル構造体自体のメモリ解放を行う。動的に確保されたハンドルの場合、このコールバック内でfree()を呼ぶのが典型的なパターンである。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（イベントループのクローズフェーズで処理） |
| 優先度 | 中（イベントループの各イテレーション終盤で処理） |
| リトライ | なし（コールバックは1回のみ呼び出される。ただしSIGNALハンドルのみ再キュー可能性あり） |

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

uv_close()呼び出し時にclose_cbパラメータとして指定されたコールバック関数が送信先となる。close_cbはuv_handle_t構造体のclose_cbフィールドに保存される（src/unix/core.c 163行目）。NULLが指定された場合、uv__finish_close()内のNULLチェック（src/unix/core.c 362行目）によりコールバック呼び出しはスキップされる。

## 通知テンプレート

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

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

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

```
handle: uv_handle_t* - クローズされたハンドル
  - handle->data: void* - ユーザ定義データ
  - handle->loop: uv_loop_t* - 所属イベントループ
  - handle->type: uv_handle_type - ハンドル型（UV_TCP, UV_TIMER等）
  - handle->flags: UV_HANDLE_CLOSED設定済み
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| handle | クローズされたハンドル | uv_close()の第1引数 | Yes |
| handle->data | ユーザ定義コンテキスト | ユーザ設定 | No |
| handle->type | ハンドル型識別子 | ハンドル初期化時に設定 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | uv_close()呼び出し | close_cb != NULL | ハンドルのクローズ要求時 |
| API呼び出し | uv_tcp_close_reset() | close_cb != NULL | TCP接続のRSTクローズ要求時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| close_cb == NULL | uv_close()でNULLが指定された場合、コールバックは呼び出されない |
| 既にクローズ中 | UV_HANDLE_CLOSINGまたはUV_HANDLE_CLOSEDフラグが設定済みの場合、uv_close()はassertで失敗する |
| SIGNALハンドルの保留シグナル | caught_signals > dispatched_signalsの場合、UV_HANDLE_CLOSEDをリセットし再キューする |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[uv_close呼び出し] --> B[UV_HANDLE_CLOSINGフラグ設定]
    B --> C[close_cbをハンドルに保存]
    C --> D[ハンドル型別クローズ処理]
    D --> E[uv__make_close_pending]
    E --> F[closing_handlesリストに追加]
    F --> G[イベントループ継続]
    G --> H[uv__run_closing_handles]
    H --> I[uv__finish_close]
    I --> J[UV_HANDLE_CLOSEDフラグ設定]
    J --> K[ハンドル型別最終処理]
    K --> L[ハンドルキューから除去]
    L --> M{close_cb != NULL?}
    M -->|Yes| N[close_cb実行]
    M -->|No| O[スキップ]
    N --> P[終了]
    O --> P
```

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

### 参照テーブル一覧

本通知はデータベースを使用しない（インメモリのリスト構造で実装）。

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | closing_handlesリンクリストで管理 |

### 内部データ構造

#### closing_handlesリスト

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| loop->closing_handles | クローズ待ちハンドルのリンクリスト先頭 | イベントループからアクセス |
| handle->next_closing | 次のクローズ待ちハンドルへのポインタ | uv__make_close_pendingで設定 |

#### ハンドルキュー

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| loop->handle_queue | 全ハンドルの双方向キュー | uv_walk()等で使用 |
| handle->handle_queue | ハンドルのキューノード | uv__finish_close()で除去 |

### 更新テーブル一覧

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

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| assertionエラー | 既にクローズ中のハンドルに対してuv_close()を呼んだ場合 | プログラム異常終了（assert失敗） |
| SIGNALハンドル再キュー | 保留シグナルが残っている場合 | UV_HANDLE_CLOSEDをリセットし、クローズリストに再挿入して次のイテレーションで再試行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 通常0回。SIGNALハンドルは保留シグナル処理完了まで自動再試行 |
| リトライ間隔 | イベントループの1イテレーション |
| リトライ対象エラー | SIGNALハンドルの保留シグナル未処理のみ |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし |
| 備考 | クローズリスト上の全ハンドルが各イテレーションで処理される |

### 配信時間帯

時間帯の制限なし。イベントループの各イテレーションの最終段階（uv__run_check()の後、uv__update_time()の前）でクローズハンドルが処理される（src/unix/core.c 475行目）。

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

- クローズ後のハンドルへのアクセスは未定義動作となる。close_cb内でハンドルを解放した後にアクセスしてはならない
- UV_HANDLE_CLOSINGフラグにより、同一ハンドルに対する二重クローズはassertで検出される（src/unix/core.c 160行目）
- ストリーム型ハンドル（TCP, Pipe, TTY）はuv__finish_close()内でuv__stream_destroy()が呼ばれ、バッファの解放が行われる
- UDPハンドルはuv__udp_finish_close()で追加のクリーンアップが行われる
- FS_EVENTハンドルはSolaris/z/OSで遅延クローズとなり、イベント配信完了後にクローズが完了する

## 備考

- uv_close()は全てのハンドル型で必須の操作であり、ハンドルを適切にクリーンアップするために呼び出す必要がある
- uv_close()は同期的に型固有のクローズ処理を開始するが、close_cbの呼び出しは常に非同期（次のイベントループイテレーション以降）である
- uv_loop_close()はアクティブなハンドルが残っている場合にUV_EBUSYを返すため、全ハンドルのクローズ完了を確認してからループを閉じる必要がある
- FS_POLLハンドルは内部でファイルシステムリクエストを使用するため、uv_close()からreturnし、ポーリングコードがuv__make_close_pending()を呼ぶ（src/unix/core.c 226-227行目）
- FS_EVENTハンドルはSolaris/z/OSで同様に遅延クローズとなる（src/unix/core.c 208-216行目）
- closing_handlesリストはイベントループが生存している（uv__loop_alive）かどうかの判定にも使用される（src/unix/core.c 397行目）

---

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

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

### 推奨読解順序

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

まず、ハンドルの共通データ構造とクローズ関連のフラグを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | 333行目: uv_close_cb型定義、463-476行目: UV_HANDLE_FIELDSマクロ（data, loop, type, close_cb, handle_queue） |
| 1-2 | uv.h | `include/uv.h` | 504行目: uv_close() API宣言 |
| 1-3 | uv-common.h | `src/uv-common.h` | 82-83行目: UV_HANDLE_CLOSING（0x01）, UV_HANDLE_CLOSED（0x02）フラグ定義、277-278行目: uv__is_closing()マクロ |

**読解のコツ**: UV_HANDLE_FIELDSマクロは全ハンドル型の構造体に展開される。close_cbフィールドはこのマクロ内（470行目）で定義されているため、全ハンドル型がclose_cbを保持する。

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

uv_close()関数の処理フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | core.c | `src/unix/core.c` | 159-238行目: uv_close()関数 - クローズ開始処理（型別分岐、uv__make_close_pending呼び出し） |

**主要処理フロー**:
1. **160行目**: assert(!uv__is_closing(handle)) - 二重クローズ防止
2. **162行目**: UV_HANDLE_CLOSINGフラグを設定
3. **163行目**: close_cbをハンドルに保存
4. **165-235行目**: switch文でハンドル型別のクローズ処理を呼び出し（13種類のハンドル型に対応）
5. **237行目**: uv__make_close_pending()でクローズリストに追加

#### Step 3: クローズリストへの追加と管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | core.c | `src/unix/core.c` | 268-273行目: uv__make_close_pending()関数 - closing_handlesリストの先頭にハンドルを追加 |

**主要処理フロー**:
- **269行目**: UV_HANDLE_CLOSINGフラグの確認
- **270行目**: UV_HANDLE_CLOSEDフラグが未設定であることの確認
- **271行目**: handle->next_closingにloop->closing_handlesを設定（リストの先頭挿入）
- **272行目**: loop->closing_handlesをhandleに更新

#### Step 4: クローズフェーズの実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | core.c | `src/unix/core.c` | 368-380行目: uv__run_closing_handles()関数 - クローズリストの全ハンドルを処理 |
| 4-2 | core.c | `src/unix/core.c` | 475行目: uv_run()内でのuv__run_closing_handles()呼び出し位置 |

**主要処理フロー**:
- **372行目**: loop->closing_handlesからリストを取得
- **373行目**: loop->closing_handlesをNULLにリセット
- **375-379行目**: リスト上の全ハンドルについてuv__finish_close()を呼び出す

#### Step 5: 最終クローズ処理とコールバック呼び出しを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | core.c | `src/unix/core.c` | 303-365行目: uv__finish_close()関数 - UV_HANDLE_CLOSED設定、型別最終処理、コールバック呼び出し |

**主要処理フロー**:
- **316行目**: UV_HANDLE_CLOSEDフラグを設定
- **318-357行目**: switch文で型別の最終クリーンアップ（ストリーム型: uv__stream_destroy、UDP: uv__udp_finish_close、SIGNAL: 保留シグナルチェック）
- **359行目**: uv__handle_unref()でハンドルの参照カウントを解除
- **360行目**: uv__queue_remove()でhandle_queueから除去
- **362-363行目**: close_cbがNULLでなければコールバックを呼び出す

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

```
uv_close()                           [src/unix/core.c:159]
    |
    +-- UV_HANDLE_CLOSINGフラグ設定    [src/unix/core.c:162]
    +-- close_cb保存                   [src/unix/core.c:163]
    +-- 型別クローズ処理               [src/unix/core.c:165-235]
    |      +-- uv__pipe_close()       [UV_NAMED_PIPE]
    |      +-- uv__tty_close()        [UV_TTY]
    |      +-- uv__tcp_close()        [UV_TCP]
    |      +-- uv__udp_close()        [UV_UDP]
    |      +-- uv__prepare_close()    [UV_PREPARE]
    |      +-- uv__check_close()      [UV_CHECK]
    |      +-- uv__idle_close()       [UV_IDLE]
    |      +-- uv__async_close()      [UV_ASYNC]
    |      +-- uv__timer_close()      [UV_TIMER]
    |      +-- uv__process_close()    [UV_PROCESS]
    |      +-- uv__fs_event_close()   [UV_FS_EVENT]
    |      +-- uv__poll_close()       [UV_POLL]
    |      +-- uv__fs_poll_close()    [UV_FS_POLL]
    |      +-- uv__signal_close()     [UV_SIGNAL]
    |
    +-- uv__make_close_pending()      [src/unix/core.c:268]
           +-- closing_handlesリストに挿入

--- イベントループ ---
uv_run()                              [src/unix/core.c:427]
    |
    +-- uv__run_closing_handles()     [src/unix/core.c:368]
           |
           +-- uv__finish_close()     [src/unix/core.c:303]
                  |
                  +-- UV_HANDLE_CLOSED設定
                  +-- 型別最終処理
                  +-- uv__handle_unref()
                  +-- uv__queue_remove()
                  +-- close_cb(handle)  [ユーザコールバック実行]
```

### データフロー図

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

uv_close(handle,   ──────> UV_HANDLE_CLOSINGフラグ設定 ──> 型別クローズ処理
  close_cb)                                                     |
                                                                v
                           uv__make_close_pending()    ──> closing_handlesリスト
                                                                |
                           [イベントループ次イテレーション]        |
                                                                v
                           uv__run_closing_handles()   ──> uv__finish_close()
                                                                |
                                                                v
                           UV_HANDLE_CLOSED設定         ──> handle_queue除去
                                                                |
                                                                v
                                                           close_cb(handle)
                                                           [ユーザコールバック]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_close_cb型定義（333行目）、UV_HANDLE_FIELDS（463行目）、uv_close() API宣言（504行目） |
| uv-common.h | `src/uv-common.h` | ヘッダ | UV_HANDLE_CLOSING/UV_HANDLE_CLOSEDフラグ定義、uv__is_closing()マクロ |
| core.c | `src/unix/core.c` | ソース | uv_close()、uv__make_close_pending()、uv__finish_close()、uv__run_closing_handles()、uv_run() |
| handle.c | `src/win/handle.c` | ソース | Windows版uv_close()実装 |
| stream.c | `src/unix/stream.c` | ソース | uv__stream_destroy() - ストリーム型ハンドルの最終クリーンアップ |
| tcp.c | `src/unix/tcp.c` | ソース | uv__tcp_close() - TCP型固有クローズ処理 |
| udp.c | `src/unix/udp.c` | ソース | uv__udp_close()、uv__udp_finish_close() - UDP型固有クローズ/最終処理 |
| pipe.c | `src/unix/pipe.c` | ソース | uv__pipe_close() - Pipe型固有クローズ処理 |
| poll.c | `src/unix/poll.c` | ソース | uv__poll_close() - Poll型固有クローズ処理 |
| timer.c | `src/timer.c` | ソース | uv__timer_close() - Timer型固有クローズ処理 |
| signal.c | `src/unix/signal.c` | ソース | uv__signal_close() - Signal型固有クローズ処理 |
| process.c | `src/unix/process.c` | ソース | uv__process_close() - Process型固有クローズ処理 |
| async.c | `src/unix/async.c` | ソース | uv__async_close() - Async型固有クローズ処理 |
| fs-poll.c | `src/fs-poll.c` | ソース | uv__fs_poll_close() - FS Poll型固有クローズ処理（遅延クローズ） |
