# 通知設計書 13-ポーリング通知

## 概要

本ドキュメントは、libuvにおけるポーリング通知（uv_poll_cb）の設計仕様を定義する。カスタムファイルディスクリプタの読み書き可能状態をI/Oポーリングで監視し、イベント発生時にコールバックで通知する仕組みについて記述する。

### 本通知の処理概要

ポーリング通知は、ユーザが指定したファイルディスクリプタ（ソケット、パイプなど）のI/O状態変化をOSのI/Oポーリング機構（epoll/kqueue/poll等）を通じて監視し、指定されたイベント（読み込み可能、書き込み可能、切断、優先データ）が発生した際にコールバックで通知する機構である。libuvが直接管理しない外部のファイルディスクリプタを、libuvのイベントループに統合するために使用される。

**業務上の目的・背景**：libuvが提供するTCP/UDP/Pipe等の標準的なI/Oハンドルでは対応できない、外部ライブラリやカスタムプロトコルが管理するファイルディスクリプタをイベントループに統合する必要がある。例えば、データベースクライアントライブラリのソケット、D-Busのファイルディスクリプタ、GPIOデバイスファイルなどを、libuvのイベントループ内で効率的に監視するためにuv_pollハンドルが提供されている。これにより、アプリケーション全体を単一のイベントループで統一的に制御できる。

**通知の送信タイミング**：uv_poll_start()で監視を開始した後、指定したイベント（UV_READABLE, UV_WRITABLE, UV_DISCONNECT, UV_PRIORITIZED）がファイルディスクリプタ上で発生したとき、イベントループのI/Oポーリングフェーズ（uv__io_poll）でイベントが検出され、uv__poll_io()を通じてコールバックが呼び出される。POLLERRが検出された場合は、I/O監視を停止しstatus=UV_EBADFでコールバックが呼ばれる。

**通知の受信者**：uv_poll_start()呼び出し時にpoll_cbパラメータとして指定されたコールバック関数。メインイベントループスレッド上で実行される。

**通知内容の概要**：コールバックには、ポーリングハンドル（uv_poll_t*）、ステータスコード（0: 正常、UV_EBADF: エラー）、検出されたイベントマスク（UV_READABLE, UV_WRITABLE, UV_DISCONNECT, UV_PRIORITIZEDのビット論理和）が渡される。ユーザはeventsパラメータで具体的にどのI/Oイベントが発生したかを判別できる。

**期待されるアクション**：受信者は検出されたイベントに応じて、ファイルディスクリプタに対する読み込み（read/recv）、書き込み（write/send）、切断処理などを実行する。エラー（UV_EBADF）の場合は、ファイルディスクリプタの有効性を確認し、必要に応じてポーリングを停止・クローズする。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（I/Oポーリングフェーズでイベント検出時に通知） |
| 優先度 | 高（I/Oイベントの即時通知） |
| リトライ | なし（イベント発生ごとにコールバックが呼び出される。レベルトリガー方式） |

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

uv_poll_start()呼び出し時にpoll_cbパラメータとして指定されたコールバック関数が送信先となる。コールバックはuv_poll_t構造体のpoll_cbフィールドに保存される（src/unix/poll.c 151行目）。uv_poll_start()でpevents=0が指定された場合、I/O監視は開始されず、コールバックも呼び出されない（src/unix/poll.c 136-137行目）。

## 通知テンプレート

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

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

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

```
handle: uv_poll_t* - ポーリングハンドル
  - handle->data: void* - ユーザ定義データ
  - handle->loop: uv_loop_t* - 所属イベントループ
  - handle->poll_cb: uv_poll_cb - 本コールバック
  - handle->io_watcher.fd: int - 監視対象ファイルディスクリプタ

status: int - ステータスコード
  - 0: 正常（イベント検出）
  - UV_EBADF: ファイルディスクリプタエラー（POLLERR検出）

events: int - 検出されたイベントマスク（ビット論理和）
  - UV_READABLE (1): 読み込み可能
  - UV_WRITABLE (2): 書き込み可能
  - UV_DISCONNECT (4): 切断検出
  - UV_PRIORITIZED (8): 優先データ到着
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| handle | ポーリングハンドル | uv_poll_init()で初期化 | Yes |
| status | ステータスコード | I/Oポーリング結果 | Yes |
| events | イベントマスク | I/Oポーリング検出イベント | Yes |
| handle->data | ユーザ定義コンテキスト | ユーザ設定 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| I/Oイベント | POLLIN検出 | UV_READABLEが監視対象 | ファイルディスクリプタが読み込み可能 |
| I/Oイベント | POLLOUT検出 | UV_WRITABLEが監視対象 | ファイルディスクリプタが書き込み可能 |
| I/Oイベント | UV__POLLRDHUP検出 | UV_DISCONNECTが監視対象 | 接続先の切断を検出 |
| I/Oイベント | UV__POLLPRI検出 | UV_PRIORITIZEDが監視対象 | 優先データ（OOB等）の到着 |
| エラーイベント | POLLERR検出 | 常時（UV__POLLPRIなし） | ファイルディスクリプタのエラー状態 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| pevents == 0 | uv_poll_start()でイベントマスクが0の場合、監視は開始されない |
| uv_poll_stop()呼び出し後 | I/O監視が停止されコールバックは呼ばれない |
| ハンドルクローズ中 | UV_HANDLE_CLOSINGフラグ設定後は新たな監視開始不可 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[uv_poll_init: ハンドル初期化] --> B[uv_poll_start: 監視開始]
    B --> C[POLLINをUV_READABLE等に変換]
    C --> D[uv__io_start: I/Oウォッチャー登録]
    D --> E[イベントループのI/Oポーリング]
    E --> F[uv__io_poll: OSポーリング実行]
    F --> G{イベント検出?}
    G -->|Yes| H[uv__io_cb: I/Oコールバックディスパッチ]
    G -->|No| E
    H --> I[uv__poll_io: ポーリングI/Oハンドラ]
    I --> J{POLLERR検出?}
    J -->|Yes, POLLPRIなし| K[uv__io_stop + uv__handle_stop]
    J -->|No| L[イベントマスク変換]
    K --> M[poll_cb: status=UV_EBADF, events=0]
    L --> N[poll_cb: status=0, events=検出イベント]
    M --> O[終了]
    N --> O
```

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

### 参照テーブル一覧

本通知はデータベースを使用しない（OSのI/Oポーリング機構で実装）。

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | OS I/Oポーリング（epoll/kqueue/poll）で管理 |

### 内部データ構造

#### uv__io_tウォッチャー

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| io_watcher.fd | 監視対象ファイルディスクリプタ | uv_poll_init()で設定 |
| io_watcher.pevents | 次のポーリングでの監視イベントマスク | uv__io_start()で更新 |
| io_watcher.events | 現在の監視イベントマスク | I/Oポーリング後に更新 |
| io_watcher.watcher_queue | ウォッチャーキューノード | uv__io_start/stopで操作 |

#### ループウォッチャー配列

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| loop->watchers | ファイルディスクリプタからウォッチャーへのマッピング配列 | fd番号でインデックス |
| loop->nwatchers | watchers配列のサイズ | maybe_resize()で拡張 |

### 更新テーブル一覧

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

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| UV_EEXIST | uv_poll_init()時にfdが既に別のウォッチャーで使用中 | エラーコードを返す |
| UV_EEXIST | uv_poll_start()時にfdが別のウォッチャーに紐付いている | エラーコードを返す |
| UV_EBADF | POLLERR検出（UV__POLLPRIなし） | I/O監視を停止し、status=UV_EBADFでコールバック呼び出し |
| fdチェックエラー | uv__io_check_fd()失敗 | uv_poll_init()でエラーコードを返す |
| 非ブロッキング設定エラー | uv__nonblock()失敗 | uv_poll_init()でエラーコードを返す |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 該当なし（レベルトリガー方式のため、イベント継続中は毎回通知） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | なし（POLLERRはI/O停止で対処） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし（イベント発生ごとにコールバック実行） |
| 備考 | レベルトリガー方式のため、条件が継続する限りイテレーションごとに通知 |

### 配信時間帯

時間帯の制限なし。イベントループのI/Oポーリングフェーズ（uv__io_poll）でイベントが検出されるたびにコールバックが実行される。uv__io_pollはuv_run()の各イテレーションで1回実行される（src/unix/core.c 460行目）。

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

- uv_poll_init()でファイルディスクリプタを非ブロッキングモードに設定する（src/unix/poll.c 80行目）。ioctl(FIONBIO)がENOTTYを返す場合はfcntl(F_GETFL/F_SETFL)にフォールバックする（src/unix/poll.c 82-84行目）
- 同一ファイルディスクリプタを複数のuv_pollハンドルで監視することはできない（UV_EEXIST）
- ファイルディスクリプタの有効性はuv__io_check_fd()で初期化時に検証される
- POLLERRとPOLLPRIの同時発生はsysfsポーリング時に起こり得る（Linuxカーネルfs/kernfs/file.c参照）。この場合、エラーではなくUV_PRIORITIZEDとして処理される（src/unix/poll.c 46行目のコメント参照）
- uv_poll_init_socket()はUnix系ではuv_poll_init()と同等の実装である（src/unix/poll.c 96-98行目）

## 備考

- uv_pollはlibuvが管理しない外部ファイルディスクリプタ用に設計されている。libuvのTCP/UDP/Pipe等のハンドルには使用すべきでない
- uv_poll_start()を再度呼び出すことで、監視イベントマスクとコールバック関数を変更できる。内部的にuv__poll_stop()してからuv__io_start()で再登録する（src/unix/poll.c 134行目）
- イベントマスク変換: UV_READABLE <-> POLLIN、UV_WRITABLE <-> POLLOUT、UV_DISCONNECT <-> UV__POLLRDHUP、UV_PRIORITIZED <-> UV__POLLPRI
- uv__platform_invalidate_fd()がuv__poll_stop()内で呼ばれ、プラットフォーム固有のfd無効化処理を行う（src/unix/poll.c 107行目）
- POLLERR検出時はstatus=UV_EBADFとevents=0でコールバックが呼ばれ、ハンドルは自動的に停止される（src/unix/poll.c 47-50行目）

---

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

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

### 推奨読解順序

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

まず、ポーリング通知に関わるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | 226行目: uv_poll_t型定義、334行目: uv_poll_cb型定義、904-908行目: uv_poll_s構造体（UV_HANDLE_FIELDS, poll_cb, UV_POLL_PRIVATE_FIELDS） |
| 1-2 | uv.h | `include/uv.h` | 910-915行目: uv_poll_event列挙型（UV_READABLE=1, UV_WRITABLE=2, UV_DISCONNECT=4, UV_PRIORITIZED=8） |
| 1-3 | unix.h | `include/uv/unix.h` | 90-98行目: uv__io_s構造体（bits, pending_queue, watcher_queue, pevents, events, fd）、309-310行目: UV_POLL_PRIVATE_FIELDS（uv__io_t io_watcher） |

**読解のコツ**: uv_poll_tはuv_handle_tのサブクラスで、UV_HANDLE_FIELDSマクロにより共通フィールドを持つ。UV_POLL_PRIVATE_FIELDSによりuv__io_tウォッチャーが埋め込まれ、container_of()マクロでuv_poll_tに変換される。uv__io_tのbitsフィールドの下位4ビットがI/Oコールバック種別（UV__POLL_IO）を格納する。

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

ポーリングハンドルの初期化と監視開始を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | poll.c | `src/unix/poll.c` | 67-93行目: uv_poll_init()関数 - fdの存在チェック、有効性検証、非ブロッキング設定、ハンドル初期化、I/Oウォッチャー初期化 |
| 2-2 | poll.c | `src/unix/poll.c` | 118-154行目: uv_poll_start()関数 - ウォッチャー重複チェック、既存監視停止、イベントマスク変換、I/O監視開始 |

**主要処理フロー**:
1. **70行目**: uv__fd_exists()でfdの重複チェック
2. **73-75行目**: uv__io_check_fd()でfdの有効性を確認
3. **80-84行目**: uv__nonblock()で非ブロッキングモードに設定
4. **89行目**: uv__handle_init()でハンドルをUV_POLL型として初期化
5. **90行目**: uv__io_init()でI/OウォッチャーをUV__POLL_IOコールバック種別で初期化
6. **134行目**: uv__poll_stop()で既存の監視を停止
7. **139-147行目**: UV_READABLE等からPOLLIN等へのイベントマスク変換
8. **149行目**: uv__io_start()でI/O監視を開始
9. **151行目**: poll_cbをハンドルに保存

#### Step 3: I/Oポーリングとイベントディスパッチを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | core.c | `src/unix/core.c` | 460行目: uv_run()内でのuv__io_poll()呼び出し |
| 3-2 | internal.h | `src/unix/internal.h` | 264-276行目: uv__io_cb_t列挙型（UV__POLL_IO = 6番目）、290行目: uv__poll_io()関数宣言 |
| 3-3 | core.c | `src/unix/core.c` | 923-924行目: uv__io_cb()内でのUV__POLL_IO分岐 -> uv__poll_io()呼び出し |

**主要処理フロー**:
- **460行目**: uv__io_poll(loop, timeout)でOSのI/Oポーリングを実行
- **923-924行目**: I/Oイベント発生時、uv__io_cb()がコールバック種別に基づきuv__poll_io()をディスパッチ

#### Step 4: ポーリングI/Oハンドラの処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | poll.c | `src/unix/poll.c` | 29-64行目: uv__poll_io()関数 - エラー処理、イベントマスク変換、コールバック呼び出し |

**主要処理フロー**:
- **33行目**: container_of()でuv__io_tからuv_poll_tを取得
- **46-51行目**: POLLERR（UV__POLLPRIなし）の場合、I/O監視停止しUV_EBADFでコールバック
- **53-61行目**: POLLIN -> UV_READABLE、UV__POLLPRI -> UV_PRIORITIZED、POLLOUT -> UV_WRITABLE、UV__POLLRDHUP -> UV_DISCONNECTへの変換
- **63行目**: handle->poll_cb(handle, 0, pevents)でコールバック呼び出し

#### Step 5: 監視停止とクローズ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | poll.c | `src/unix/poll.c` | 102-108行目: uv__poll_stop()関数 - I/O監視停止、ハンドル停止、fd無効化 |
| 5-2 | poll.c | `src/unix/poll.c` | 111-115行目: uv_poll_stop()関数 - 公開API |
| 5-3 | poll.c | `src/unix/poll.c` | 157-159行目: uv__poll_close()関数 - クローズ時のI/O監視停止 |

**主要処理フロー**:
- **103-105行目**: uv__io_stop()でPOLLIN|POLLOUT|UV__POLLRDHUP|UV__POLLPRIの全イベント監視を停止
- **106行目**: uv__handle_stop()でハンドルの非アクティブ化
- **107行目**: uv__platform_invalidate_fd()でプラットフォーム固有のfd無効化

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

```
uv_poll_init()                       [src/unix/poll.c:67]
    |
    +-- uv__fd_exists()              [fd重複チェック]
    +-- uv__io_check_fd()            [fd有効性確認]
    +-- uv__nonblock()               [非ブロッキング設定]
    +-- uv__handle_init()            [ハンドル初期化]
    +-- uv__io_init()                [I/Oウォッチャー初期化, UV__POLL_IO]

uv_poll_start()                      [src/unix/poll.c:118]
    |
    +-- uv__poll_stop()              [src/unix/poll.c:102 - 既存監視停止]
    +-- uv__io_start()               [src/unix/core.c:953 - I/O監視開始]
    +-- uv__handle_start()           [ハンドルアクティブ化]

--- イベントループ ---
uv_run()                              [src/unix/core.c:427]
    |
    +-- uv__io_poll()                [OS I/Oポーリング実行]
           |
           +-- uv__io_cb()           [src/unix/core.c:900 - コールバックディスパッチ]
                  |
                  +-- uv__poll_io()  [src/unix/poll.c:29]
                         |
                         +-- poll_cb(handle, status, events)  [ユーザコールバック]

uv_poll_stop()                       [src/unix/poll.c:111]
    |
    +-- uv__poll_stop()              [src/unix/poll.c:102]
           +-- uv__io_stop()         [I/O監視停止]
           +-- uv__handle_stop()     [ハンドル非アクティブ化]
           +-- uv__platform_invalidate_fd()  [fd無効化]
```

### データフロー図

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

uv_poll_init(fd)   ──────> uv__io_init()             ──> io_watcher設定
                           uv__nonblock(fd)           ──> fdを非ブロッキングに

uv_poll_start      ──────> イベントマスク変換          ──> POLLIN/POLLOUT等
(events, poll_cb)          uv__io_start()             ──> OS I/Oポーリング登録
                                    |
                           [イベントループ]                  |
                                    v
                           uv__io_poll()              ──> OSイベント検出
                                    |
                                    v
                           uv__poll_io()              ──> イベントマスク逆変換
                           (POLLIN -> UV_READABLE等)       |
                                    |                      v
                                    +─────────────────> poll_cb(handle,
                                                         status, events)
                                                        [ユーザコールバック]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_poll_t構造体（904行目）、uv_poll_cb型定義（334行目）、uv_poll_event列挙型（910行目）、API宣言（917-922行目） |
| unix.h | `include/uv/unix.h` | ヘッダ | uv__io_s構造体（90行目）、UV_POLL_PRIVATE_FIELDS（309行目） |
| internal.h | `src/unix/internal.h` | ヘッダ | uv__io_cb_t列挙型（264行目）、uv__poll_io()宣言（290行目）、I/O関連関数宣言 |
| poll.c | `src/unix/poll.c` | ソース | ポーリング通知の全実装（uv_poll_init, uv_poll_start, uv_poll_stop, uv__poll_io, uv__poll_close） |
| core.c | `src/unix/core.c` | ソース | uv__io_init()（941行目）、uv__io_start()（953行目）、uv__io_stop()（1005行目）、uv__io_cb()ディスパッチ（923行目）、uv_run()（427行目） |
| kqueue.c | `src/unix/kqueue.c` | ソース | macOS/BSD向けuv__io_poll()実装 |
| linux.c | `src/unix/linux.c` | ソース | Linux向けuv__io_poll()実装（epoll） |
| posix-poll.c | `src/unix/posix-poll.c` | ソース | POSIX poll()を使用したuv__io_poll()実装 |
| sunos.c | `src/unix/sunos.c` | ソース | Solaris向けuv__io_poll()実装（event ports） |
