# 機能設計書 116-FSイベント監視開始・停止

## 概要

本ドキュメントは、libuvのファイルシステムイベント監視開始・停止機能 `uv_fs_event_start()` / `uv_fs_event_stop()` の設計仕様を記述する。カーネルベースのファイル変更監視の制御について定義する。

### 本機能の処理概要

`uv_fs_event_start()` は、指定されたファイルまたはディレクトリの変更を監視し、変更が発生した際にコールバック関数を呼び出す。`uv_fs_event_stop()` は監視を停止する。カーネルのファイルシステム通知メカニズム（kqueue/inotify/FSEvents等）を利用して効率的な変更検知を行う。

**業務上の目的・背景**：アプリケーションがファイルの変更（内容変更、リネーム、削除等）をリアルタイムに検知する必要がある場面で、ポーリングよりも効率的なカーネル通知メカニズムを提供する。ホットリロード機能、ファイル同期ツール、IDE連携等で重要な役割を果たす。

**機能の利用シーン**：ソースコードの変更検知によるホットリロード、設定ファイルの変更検知による自動再読み込み、ファイル同期サービス、ログファイルのローテーション検知など。

**主要な処理内容**：
1. (start) ハンドルのアクティブ状態チェック
2. (start) コールバック設定とパスの複製
3. (start) 対象ファイル/ディレクトリのopen()
4. (start) macOS: ディレクトリの場合FSEventsバックエンド使用、それ以外はkqueue
5. (start) kqueue: I/Oウォッチャーの初期化・登録
6. (stop) ハンドルのアクティブ状態チェック
7. (stop) macOS: FSEventsの場合はuv__fsevents_close、kqueueの場合はI/O close
8. (stop) パスの解放

**関連システム・外部連携**：kqueue(macOS/BSD)、inotify(Linux)、FSEvents(macOS)、ReadDirectoryChangesW(Windows)。

**権限による制御**：監視対象ファイル/ディレクトリへの読み取りアクセス権が必要（open(O_RDONLY)で開く）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | ファイルシステムイベント | 主機能 | uv_fs_event_start()/stop()によるファイル変更監視制御のAPI定義 |

## 機能種別

イベント監視処理（ファイルシステム変更通知の開始・停止）

## 入力仕様

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

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| handle | uv_fs_event_t* | Yes | FSイベントハンドル（初期化済み） | アクティブでないこと |
| cb | uv_fs_event_cb | Yes | 変更検知時のコールバック | NULLでないこと |
| path | const char* | Yes | 監視対象のファイル/ディレクトリパス | NULLでないこと |
| flags | unsigned int | Yes | 監視フラグ（UV_FS_EVENT_WATCH_ENTRY, UV_FS_EVENT_STAT, UV_FS_EVENT_RECURSIVE） | 有効なフラグ |

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

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| handle | uv_fs_event_t* | Yes | FSイベントハンドル | - |

### 入力データソース

ユーザーが指定するファイルまたはディレクトリのパス文字列。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| start戻り値 | int | 成功時は0、エラー時は負のエラーコード |
| stop戻り値 | int | 成功時は0 |
| コールバック引数 | handle, filename, events, status | 変更検知時に通知される情報 |

### 出力先

コールバック関数へのイベント通知。eventsはUV_RENAME（リネーム/削除）またはUV_CHANGE（属性/内容変更）。

## 処理フロー

### 処理シーケンス（start - kqueue実装）

```
1. アクティブチェック
   └─ 既にアクティブならUV_EINVALを返す
2. コールバック設定
   └─ handle->cb = cb
3. パス複製
   └─ handle->path = uv__strdup(path)
4. ファイルオープン
   └─ fd = open(handle->path, O_RDONLY)
5. macOS: ディレクトリ判定
   ├─ ディレクトリ → FSEventsバックエンド（uv__fsevents_init）
   └─ ファイル → kqueueバックエンド
6. kqueue: I/Oウォッチャー初期化
   └─ uv__io_init_start(handle->loop, &handle->event_watcher, UV__FS_EVENT, fd, POLLIN)
7. ハンドルアクティブ化
   └─ uv__handle_start(handle)
```

### フローチャート

```mermaid
flowchart TD
    A[uv_fs_event_start] --> B{既にアクティブ?}
    B -->|Yes| C[return UV_EINVAL]
    B -->|No| D[handle->cb = cb]
    D --> E[handle->path = uv__strdup path]
    E --> F{メモリ確保成功?}
    F -->|No| G[return UV_ENOMEM]
    F -->|Yes| H[fd = open path O_RDONLY]
    H --> I{open成功?}
    I -->|No| J[パス解放, return エラー]
    I -->|Yes| K{macOS かつ ディレクトリ?}
    K -->|Yes| L[FSEvents初期化]
    K -->|No| M[kqueue: I/Oウォッチャー初期化]
    L --> N[uv__handle_start]
    M --> N
    N --> O[return 0]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-116-1 | 二重開始防止 | 既にアクティブなハンドルでstartを呼ぶとUV_EINVALを返す | start呼び出し時 |
| BR-116-2 | ワンショット再武装 | kqueueはワンショットモードで動作し、イベント発生後に自動再登録する | kqueue実装 |
| BR-116-3 | macOSバックエンド選択 | ディレクトリ監視時はFSEvents、ファイル監視時はkqueueを使用 | macOS環境 |
| BR-116-4 | イベント種別判定 | NOTE_ATTRIB/NOTE_EXTENDはUV_CHANGE、その他はUV_RENAME | kqueue実装 |
| BR-116-5 | 非アクティブstop | 非アクティブなハンドルでstopを呼んでも0を返す | stop呼び出し時 |

### 計算ロジック

特になし。

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

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | パラメータ不正 | 既にアクティブなハンドルでstart | 先にstopを呼ぶ |
| UV_ENOMEM | メモリ不足 | パスのstrdup失敗 | メモリを確保する |
| UV_ENOENT | ファイル未存在 | 指定パスにファイルが存在しない | パスを確認する |
| UV_EACCES | アクセス拒否 | ファイルの読み取り権限がない | 権限を確認する |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- カーネルベースの通知メカニズムを使用するため、ポーリングと比較してCPU使用率が大幅に低い
- kqueueのワンショットモードにより、イベント発生ごとにkeventの再登録が行われる
- FSEventsはディレクトリ単位の監視に最適化されている

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

- 監視対象ファイルへの読み取りアクセス権が必要
- ファイルディスクリプタはclose-on-execフラグなしで開かれる可能性がある

## 備考

- UV_FS_EVENT_WATCH_ENTRYとUV_FS_EVENT_STATフラグは現在未実装（uv.h L1748, L1757のコメント参照）
- UV_FS_EVENT_RECURSIVEはmacOS(FSEvents)等のサポートプラットフォームでのみ有効
- kqueue実装では、ファイル削除後もF_GETPATH（macOS）またはF_KINFO（FreeBSD）でパスを取得可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **L1742-1767**: uv_fs_event_flags。WATCH_ENTRY(1), STAT(2), RECURSIVE(4) |
| 1-2 | uv.h | `include/uv.h` | **L1771-1775**: uv_fs_event_start()/stop()のAPI宣言 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | kqueue.c | `src/unix/kqueue.c` | **L543-609**: uv_fs_event_start()のkqueue実装 |

**主要処理フロー**:
1. **L553-554**: uv__is_activeチェック。アクティブならUV_EINVAL
2. **L556-559**: コールバック設定、パスstrdup
3. **L562-567**: open(O_RDONLY)でファイルオープン
4. **L569-597**: macOS: ディレクトリならFSEvents、ファイルならkqueue
5. **L599-603**: uv__io_init_startでI/Oウォッチャー登録
6. **L605-606**: uv__handle_start

#### Step 3: イベントコールバックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | kqueue.c | `src/unix/kqueue.c` | **L475-534**: uv__fs_event()コールバック処理 |

**主要処理フロー**:
- **L487-490**: NOTE_ATTRIB/NOTE_EXTEND → UV_CHANGE、その他 → UV_RENAME
- **L493-519**: F_GETPATH/F_KINFOでパス取得
- **L521**: handle->cb呼び出し
- **L527-533**: ワンショット再武装（kevent再登録）

#### Step 4: stop関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | kqueue.c | `src/unix/kqueue.c` | **L612-638**: uv_fs_event_stop()の実装 |

**主要処理フロー**:
- **L616-617**: 非アクティブなら0を返す
- **L619**: uv__handle_stop
- **L621-625**: macOS: FSEventsの場合はuv__fsevents_close
- **L628-631**: I/Oウォッチャーのclose、FDのclose
- **L634-635**: パスの解放

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

```
uv_fs_event_start()                    [src/unix/kqueue.c L543]
    |
    +-- uv__strdup(path)               [パス複製]
    +-- open(path, O_RDONLY)           [ファイルオープン]
    +-- uv__fsevents_init()            [macOS: FSEvents初期化]
    +-- uv__io_init_start()            [kqueue: I/Oウォッチャー登録]
    +-- uv__handle_start()             [ハンドルアクティブ化]

uv__fs_event()                         [src/unix/kqueue.c L475]
    |                                  (kqueueイベント発生時に呼ばれる)
    +-- handle->cb()                   [ユーザーコールバック]
    +-- kevent()                       [ワンショット再武装]

uv_fs_event_stop()                     [src/unix/kqueue.c L612]
    |
    +-- uv__handle_stop()
    +-- uv__fsevents_close()           [macOS: FSEvents停止]
    +-- uv__io_close()                 [I/Oウォッチャー停止]
    +-- uv__close(fd)                  [FDクローズ]
    +-- uv__free(handle->path)         [パス解放]
```

### データフロー図

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

path (監視対象)  ────> open() → kqueue/FSEvents登録  ──> コールバック通知
flags            ────>                                ──> events (UV_CHANGE/UV_RENAME)
                       カーネルイベント検知時:         ──> filename (変更ファイル名)
                       uv__fs_event() → handle->cb()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | API宣言、フラグ定義 |
| kqueue.c | `src/unix/kqueue.c` | ソース | macOS/BSDのkqueue実装 |
| linux.c | `src/unix/linux.c` | ソース | Linux(inotify)実装 |
| uv-common.c | `src/uv-common.c` | ソース | uv_fs_event_getpath()の共通実装 |
| fs-event.c | `src/win/fs-event.c` | ソース | Windows実装 |
| no-fsevents.c | `src/unix/no-fsevents.c` | ソース | FSEvents未対応環境のスタブ |
