# 通知設計書 4-ファイルシステムポーリング通知

## 概要

本ドキュメントは、libuvにおけるファイルシステムポーリング通知（uv_fs_poll_cb）の設計を記述する。タイマーベースで定期的にstat()を実行し、ファイルの変更を検出して通知するポーリング方式のファイル監視メカニズムについて定義する。

### 本通知の処理概要

uv_fs_poll_cbは、指定された間隔でuv_fs_stat()を非同期的に実行し、前回の結果と比較してファイルの変更を検出するポーリング方式のファイル監視メカニズムである。uv_fs_event（No.3）がOS固有のカーネル通知に依存するのに対し、本通知はstat()システムコールによるポーリングのため、NFSやSMB等のネットワークファイルシステムでも動作する。

**業務上の目的・背景**：OS固有のファイル監視API（inotify, kqueue, FSEvents）はローカルファイルシステムでは効率的に動作するが、NFSやSMB等のリモートファイルシステムでは機能しない場合がある。本通知は、タイマーベースのポーリングにより、ファイルシステムの種類に依存しない汎用的なファイル変更検出を提供する。

**通知の送信タイミング**：uv_fs_poll_start()で指定されたinterval（ミリ秒）ごとにstat()が実行され、前回のuv_stat_tとの比較で差異が検出された場合にコールバックが呼び出される。stat()自体がエラーを返した場合（ファイル削除等）もコールバックが呼ばれる。

**通知の受信者**：uv_fs_poll_start()で監視を開始したuv_fs_poll_tハンドルに設定されたコールバック関数（uv_fs_poll_cb）が受信者となる。

**通知内容の概要**：コールバックにはuv_fs_poll_tハンドル、ステータス（0=変更あり、負の値=stat失敗）、前回のstat結果（const uv_stat_t* prev）、今回のstat結果（const uv_stat_t* curr）が渡される。

**期待されるアクション**：受信者はprevとcurrの比較結果に基づいて適切な処理を行う。例えばファイル内容の再読み込み、キャッシュの無効化等。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（タイマー + 非同期stat()によるポーリング） |
| 優先度 | 中（タイマーおよびスレッドプールで処理される） |
| リトライ | なし |

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

poll_ctx構造体に保持されたpoll_cbフィールドを通じてコールバックが決定される。poll_ctx->parent_handleを通じてuv_fs_poll_tハンドルにアクセスし、コールバックパラメータとして渡される。

## 通知テンプレート

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

| 項目 | 内容 |
|-----|------|
| 関数型 | `typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr)` |
| 定義場所 | `include/uv.h` 409-412行目 |

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

```
handle: uv_fs_poll_t* - ファイルポーリングハンドル
status: int - ステータス（0=成功、負の値=stat失敗のエラーコード）
prev: const uv_stat_t* - 前回のstat結果
curr: const uv_stat_t* - 今回のstat結果（statusが0の場合）/ ゼロ初期化（エラーの場合）
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| handle | ポーリングハンドル | poll_ctx->parent_handle | Yes |
| status | stat結果のステータス | fs_req.result | Yes |
| prev | 前回のstat結果 | poll_ctx->statbuf | Yes |
| curr | 今回のstat結果 | fs_req.statbuf / zero_statbuf | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| タイマー満了 | interval経過後のstat()完了 | ファイル内容に変更あり | statbuf_eq()がfalseを返す場合 |
| stat失敗 | stat()のエラー | 前回と異なるエラーコード | busy_polling != req->resultの場合（fs-poll.c 201行目） |
| 初回stat完了 | 最初のstat()完了 | busy_polling < 0（初期値） | 初回は必ずコールバック実行（fs-poll.c 213-215行目） |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| stat結果が前回と同一 | statbuf_eq()がtrueの場合、コールバックは呼ばれない |
| ハンドル非アクティブ | uv_is_active(handle)がfalseの場合（fs-poll.c 197行目） |
| ハンドルクローズ中 | uv__is_closing(handle)がtrueの場合（fs-poll.c 197行目） |
| 同一エラーの連続 | stat失敗のエラーコードが前回と同じ場合（fs-poll.c 201行目） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[uv_fs_poll_start] --> B[初回stat実行]
    B --> C[poll_cb コールバック]
    C --> D{ハンドルアクティブ?}
    D -->|No| E[クリーンアップ]
    D -->|Yes| F{stat成功?}
    F -->|No| G{エラーコード変化?}
    G -->|Yes| H[uv_fs_poll_cb呼び出し - エラー通知]
    G -->|No| I[タイマー再スケジュール]
    F -->|Yes| J{stat結果変化?}
    J -->|Yes| K[uv_fs_poll_cb呼び出し - 変更通知]
    J -->|No| I
    H --> I
    K --> I
    I --> L[interval後にtimer_cb]
    L --> M[stat再実行]
    M --> C
```

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

### 内部データ構造

| データ構造 | 用途 | 備考 |
|-----------|------|------|
| struct poll_ctx | ポーリングコンテキスト | timer_handle, fs_req, statbuf, path, interval等を保持 |
| poll_ctx->statbuf | 前回のstat結果 | uv_stat_t型、比較用に保持 |
| poll_ctx->busy_polling | ポーリング状態フラグ | 0=未開始、1=ポーリング中、負の値=前回エラーコード |
| poll_ctx->timer_handle | 内部タイマーハンドル | UV_HANDLE_INTERNALフラグ付き |
| poll_ctx->fs_req | 非同期stat要求 | uv_fs_t型 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| UV_ENOMEM | poll_ctx割当失敗 | エラーコードを返却（fs-poll.c 82-83行目） |
| stat失敗 | ファイルが存在しない等 | コールバックにエラーコードとzero_statbufを渡す |
| タイマー開始失敗 | uv_timer_start失敗 | abort()で異常終了（fs-poll.c 232-233行目） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無限（タイマーで定期的にポーリング継続） |
| リトライ間隔 | uv_fs_poll_start()で指定されたinterval（ミリ秒） |
| リトライ対象エラー | stat()のエラー（ファイル削除後の復帰検出にも対応） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | interval依存（例: interval=1000msなら最大60回/分） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。intervalが0の場合は1msに補正される（fs-poll.c 87行目: `interval ? interval : 1`）。

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

- stat()の実行にはファイルへの読み取りアクセス権限が必要
- パス文字列はpoll_ctx内にコピーされるため、ユーザーが渡した文字列の寿命に依存しない

## 備考

- statbuf_eq()はst_ctim, st_mtim, st_birthtim, st_size, st_mode, st_uid, st_gid, st_ino, st_dev, st_flags, st_genの14フィールドを比較する（fs-poll.c 261-276行目）
- intervalからstat()の実行時間を差し引いてタイマーを再スケジュールする（fs-poll.c 229-230行目）
- poll_ctx->previousによりstart/stopの繰り返しで古いコンテキストのリンクリストが管理される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | 409-412行目: uv_fs_poll_cb型定義、380-401行目: uv_stat_t構造体 |
| 1-2 | fs-poll.c | `src/fs-poll.c` | 37-49行目: struct poll_ctx定義（timer_handle, fs_req, statbuf, busy_polling, interval, path, previous） |

**読解のコツ**: poll_ctxはuv_fs_poll_tとは別の構造体で、ヒープに動的確保される。path[1]は可変長配列パターン（flexible array member）。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fs-poll.c | `src/fs-poll.c` | 59-63行目: uv_fs_poll_init() |
| 2-2 | fs-poll.c | `src/fs-poll.c` | 66-113行目: uv_fs_poll_start() |

**主要処理フロー**:
1. **80-83行目**: poll_ctxの動的確保
2. **85-90行目**: コンテキスト初期化（loop, cb, interval, start_time, parent_handle, path）
3. **92-97行目**: 内部タイマー初期化（UV_HANDLE_INTERNALフラグ付き）
4. **99行目**: 初回stat実行（uv_fs_stat → poll_cb）
5. **103-105行目**: previousリンクリスト管理

#### Step 3: ポーリングサイクルを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fs-poll.c | `src/fs-poll.c` | 188-234行目: poll_cb() - stat完了時のコールバック、変更検出ロジック |
| 3-2 | fs-poll.c | `src/fs-poll.c` | 175-185行目: timer_cb() - タイマー満了時のstat再実行 |
| 3-3 | fs-poll.c | `src/fs-poll.c` | 261-276行目: statbuf_eq() - stat結果の比較関数 |

**主要処理フロー**:
- **200-208行目**: statエラー時の処理（エラーコード変化時のみコールバック呼び出し）
- **213-215行目**: stat成功時の変更検出（busy_polling < 0 または statbuf不一致）
- **229-230行目**: interval補正（stat実行時間を差し引き）
- **232行目**: タイマー再スケジュール

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

```
uv_fs_poll_init()
    +-- uv__handle_init()

uv_fs_poll_start()
    +-- uv__calloc() → poll_ctx確保
    +-- uv_timer_init() → 内部タイマー
    +-- uv_fs_stat() → 初回stat
    |
    +-- poll_cb()          ← stat完了コールバック
    |       +-- statbuf_eq()  ← 変更検出
    |       +-- ctx->poll_cb()  ← ユーザーコールバック（変更時のみ）
    |       +-- uv_timer_start() → タイマー再スケジュール
    |
    +-- timer_cb()         ← タイマー満了コールバック
            +-- uv_fs_stat() → stat再実行
            +-- → poll_cb()に戻る
```

### データフロー図

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

タイマー満了       timer_cb()
(interval ms) ──▶  uv_fs_stat() 実行
                         │
                         ▼
                  [スレッドプールでstat()]
                         │
                         ▼
                  poll_cb() コールバック
                         │
                         ▼
                  statbuf_eq(prev, curr)
                         │
                  ┌──────┴──────┐
                  │             │
               変更あり      変更なし
                  │             │
                  ▼             ▼
           poll_cb呼出し   タイマー再スケジュール
           (prev, curr)     のみ
                  │
                  ▼
           タイマー再スケジュール
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | uv_fs_poll_cb型定義、uv_stat_t構造体定義、公開API宣言 |
| fs-poll.c | `src/fs-poll.c` | ソース | ファイルポーリング通知の全実装（クロスプラットフォーム共通） |
| timer.c | `src/timer.c` | ソース | 内部タイマーの実装（poll_ctxが使用） |
| threadpool.c | `src/threadpool.c` | ソース | stat()の非同期実行基盤 |
| uv-common.h | `src/uv-common.h` | ヘッダ | container_ofマクロ等の共通定義 |
