# 機能設計書 119-FSポーリング開始・停止

## 概要

本ドキュメントは、libuvのファイル変更ポーリング開始・停止機能 `uv_fs_poll_start()` / `uv_fs_poll_stop()` の設計仕様を記述する。stat()ベースの定期ポーリングによるファイル変更検知の制御について定義する。

### 本機能の処理概要

`uv_fs_poll_start()` は、指定されたファイルに対して定期的にstat()を実行し、前回の結果との差異を検出した場合にコールバックを呼び出す。`uv_fs_poll_stop()` はポーリングを停止する。内部でタイマーハンドルとuv_fs_stat()リクエストを組み合わせて動作する。

**業務上の目的・背景**：カーネルベースのファイルイベント通知（inotify/kqueue）が動作しないNFS等のネットワークファイルシステム上でもファイル変更を検知できる汎用的なメカニズムを提供する。ポーリング間隔を調整することでCPU使用率とレスポンス時間のバランスを制御できる。

**機能の利用シーン**：NFSマウント上のファイル監視、ネットワーク共有ファイルの変更検知、設定ファイルの定期チェック、FSイベントが信頼できない環境でのフォールバック監視。

**主要な処理内容**：
1. (start) ポーリングコンテキスト（poll_ctx）の確保・初期化
2. (start) タイマーハンドルの初期化（UV_HANDLE_INTERNAL）
3. (start) 初回stat()リクエストの発行
4. (start) ハンドルのアクティブ化
5. (poll_cb) stat結果の比較、変更検知時のユーザーコールバック呼び出し
6. (poll_cb) タイマーによる次回stat()のスケジューリング
7. (stop) タイマー停止・コンテキスト解放

**関連システム・外部連携**：libuvタイマー機構、uv_fs_stat()（スレッドプールでのstat(2)実行）。

**権限による制御**：監視対象ファイルへのstat()実行権限が必要（パスの各ディレクトリの実行権限）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 23 | ファイルシステムポーリング | 主機能 | uv_fs_poll_start()/stop()によるファイル変更ポーリング制御のAPI定義 |

## 機能種別

イベント監視処理（stat()ベースの定期ポーリングによるファイル変更検知）

## 入力仕様

### 入力パラメータ（start）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| handle | uv_fs_poll_t* | Yes | FSポーリングハンドル（初期化済み） | アクティブでないこと |
| cb | uv_fs_poll_cb | Yes | 変更検知時のコールバック | NULLでないこと |
| path | const char* | Yes | 監視対象のファイルパス | NULLでないこと |
| interval | unsigned int | Yes | ポーリング間隔（ミリ秒） | 0の場合は1に補正 |

### 入力パラメータ（stop）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| handle | uv_fs_poll_t* | Yes | FSポーリングハンドル | - |

### 入力データソース

ユーザーが指定するファイルパスとポーリング間隔。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| start戻り値 | int | 成功時は0、エラー時は負のエラーコード |
| stop戻り値 | int | 常に0 |
| コールバック引数 | handle, status, prev, curr | 変更検知時の前回/今回stat情報 |

### 出力先

コールバック関数へのstat結果の通知。prevは前回のuv_stat_t、currは今回のuv_stat_t。

## 処理フロー

### 処理シーケンス（start）

```
1. アクティブチェック
   └─ 既にアクティブなら0を返す（再開始は静かに無視）
2. ポーリングコンテキスト確保
   └─ uv__calloc(1, sizeof(*ctx) + len) - パス文字列分を追加確保
3. コンテキスト初期化
   └─ loop, poll_cb, interval（0→1に補正）, start_time, parent_handle, path
4. タイマー初期化
   └─ uv_timer_init(loop, &ctx->timer_handle)
   └─ UV_HANDLE_INTERNALフラグ設定、参照カウント解除
5. 初回stat()実行
   └─ uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb)
6. コンテキスト管理
   └─ 既存ctx→previousリンク、handle->poll_ctx = ctx
7. ハンドルアクティブ化
   └─ uv__handle_start(handle)
```

### 処理シーケンス（stop）

```
1. アクティブチェック
   └─ 非アクティブなら0を返す
2. タイマー停止
   └─ タイマーがアクティブならuv_close(&ctx->timer_handle, timer_close_cb)
3. ハンドル停止
   └─ uv__handle_stop(handle)
```

### フローチャート

```mermaid
flowchart TD
    A[uv_fs_poll_start] --> B{既にアクティブ?}
    B -->|Yes| C[return 0]
    B -->|No| D[poll_ctx確保: calloc]
    D --> E{確保成功?}
    E -->|No| F[return UV_ENOMEM]
    E -->|Yes| G[ctx初期化: loop, cb, interval, path]
    G --> H[uv_timer_init]
    H --> I[タイマーをINTERNALに設定]
    I --> J[初回 uv_fs_stat 実行]
    J --> K{stat成功?}
    K -->|No| L[ctx解放, return エラー]
    K -->|Yes| M[handle->poll_ctx = ctx]
    M --> N[uv__handle_start]
    N --> O[return 0]

    P[poll_cb: stat完了] --> Q{ハンドルアクティブ?}
    Q -->|No| R[タイマーclose, return]
    Q -->|Yes| S{stat成功?}
    S -->|No| T{エラー変化?}
    T -->|Yes| U[コールバック: エラー通知]
    T -->|No| V[skip]
    S -->|Yes| W{stat変化?}
    W -->|Yes| X[コールバック: 変更通知]
    W -->|No| Y[skip]
    U --> Z[タイマーで次回statスケジュール]
    V --> Z
    X --> Z
    Y --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-119-1 | 間隔0補正 | interval=0は1ミリ秒に補正される | start呼び出し時 |
| BR-119-2 | 二重開始無視 | 既にアクティブなハンドルでstartを呼ぶと0を返す（エラーにならない） | start呼び出し時 |
| BR-119-3 | 変更検知基準 | st_ctim, st_mtim, st_birthtim, st_size, st_mode, st_uid, st_gid, st_ino, st_dev, st_flags, st_genの変化を検知 | poll_cbでのstatbuf比較時 |
| BR-119-4 | タイマー遅延補正 | stat()の実行時間をポーリング間隔から差し引く | poll_cb内のタイマースケジュール時 |
| BR-119-5 | 内部タイマー | タイマーハンドルはUV_HANDLE_INTERNALで参照カウントに影響しない | 常時 |
| BR-119-6 | 初回通知抑制 | 初回stat()の結果は通知せず、2回目以降の変化のみ通知する | busy_polling==0の時 |

### 計算ロジック

タイマー遅延補正: `interval -= (uv_now(ctx->loop) - ctx->start_time) % interval`

statbuf比較関数(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フィールドを比較。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_ENOMEM | メモリ不足 | poll_ctxの確保失敗 | メモリを確保する |
| 各種stat()エラー | FS操作エラー | stat()実行時のエラー | コールバックのstatusで通知される |

### リトライ仕様

stat()エラーが発生しても自動的にタイマーで次回ポーリングがスケジュールされる。エラー状態が変化した場合のみコールバックに通知される。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- stat()はスレッドプールで実行されるため、イベントループをブロックしない
- ポーリング間隔の最小値は1ミリ秒（interval=0の場合の補正値）
- stat()実行時間がポーリング間隔から差し引かれるため、実際のポーリング周期はintervalに近づく
- タイマーはUV_HANDLE_INTERNALとして管理され、ループの参照カウントに影響しない

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

- 監視対象パスへのstat()実行権限が必要
- シンボリックリンクの場合、stat()はリンク先を辿る

## 備考

- FSポーリングはFSイベント（inotify/kqueue）と比較してCPU使用率が高いため、可能な場合はuv_fs_event_*の使用を推奨
- start/stopを繰り返した場合、previousリンクリストで古いコンテキストが管理され、タイマーclose時に順次解放される
- timer_close_cbでコンテキストのリンクリストが走査・解放される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **L1700-1707**: uv_fs_poll_s構造体 |
| 1-2 | fs-poll.c | `src/fs-poll.c` | **L37-49**: struct poll_ctx。timer_handle, fs_req, statbuf, previous, path[1] |

**読解のコツ**: poll_ctxの可変長path[1]フィールドは、uv__calloc(1, sizeof(*ctx) + len)で実際のパス長分を追加確保する。previousフィールドはstart/stop繰り返し時の旧コンテキスト管理用リンクリスト。

#### Step 2: start関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fs-poll.c | `src/fs-poll.c` | **L66-113**: uv_fs_poll_start()の実装 |

**主要処理フロー**:
1. **L75-76**: uv_is_activeチェック。アクティブなら0を返す
2. **L80**: uv__calloc(1, sizeof(*ctx) + len) でコンテキスト確保
3. **L85-90**: コンテキストの各フィールド初期化
4. **L87**: interval = interval ? interval : 1（0→1補正）
5. **L92-94**: uv_timer_init
6. **L96-97**: UV_HANDLE_INTERNALフラグ設定、参照カウント解除
7. **L99-101**: 初回uv_fs_stat発行
8. **L103-106**: previousリンク設定、poll_ctx更新、handle_start

#### Step 3: ポーリングコールバックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fs-poll.c | `src/fs-poll.c` | **L188-234**: poll_cb関数 |

**主要処理フロー**:
- **L197-198**: アクティブ/クローズ中チェック
- **L200-208**: statエラー時：エラー状態が変化した場合のみコールバック通知
- **L213-215**: stat成功時：初回以降でstatbufが変化した場合のみコールバック通知
- **L217-218**: statbufの更新、busy_polling=1に設定
- **L229-230**: タイマー遅延補正（stat実行時間を差し引く）
- **L232-233**: 次回タイマースケジュール

#### Step 4: statbuf比較関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | fs-poll.c | `src/fs-poll.c` | **L261-276**: statbuf_eq関数。14フィールドの一致比較 |

#### Step 5: stop関数とクリーンアップを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | fs-poll.c | `src/fs-poll.c` | **L116-135**: uv_fs_poll_stop() |
| 5-2 | fs-poll.c | `src/fs-poll.c` | **L237-258**: timer_close_cb。previousリンクリストの走査・解放 |

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

```
uv_fs_poll_start()                    [src/fs-poll.c L66]
    |
    +-- uv__calloc()                  [コンテキスト確保]
    +-- uv_timer_init()              [内部タイマー初期化]
    +-- uv_fs_stat()                 [初回stat発行]
    |    |
    |    +-- poll_cb()               [src/fs-poll.c L188]
    |         |
    |         +-- ctx->poll_cb()     [ユーザーコールバック（変更検知時）]
    |         +-- uv_fs_req_cleanup()
    |         +-- uv_timer_start()   [次回タイマースケジュール]
    |              |
    |              +-- timer_cb()    [src/fs-poll.c L175]
    |                   |
    |                   +-- uv_fs_stat() [次回stat発行]
    +-- uv__handle_start()

uv_fs_poll_stop()                     [src/fs-poll.c L116]
    |
    +-- uv_close(timer_handle)       [タイマーclose]
    |    |
    |    +-- timer_close_cb()        [src/fs-poll.c L237]
    |         +-- uv__free(ctx)      [コンテキスト解放]
    +-- uv__handle_stop()
```

### データフロー図

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

path (監視対象)   ──> stat() → statbuf比較               ──> poll_cb(handle, status, prev, curr)
interval (間隔)   ──> タイマー → stat() → 比較 → タイマー ──> 変更検知時のみコールバック
                      (定期繰り返し)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | API宣言、uv_fs_poll_s構造体定義 |
| fs-poll.c | `src/fs-poll.c` | ソース | FSポーリング全実装（init/start/stop/getpath/コールバック） |
