# 機能設計書 116-FileWatching

## 概要

本ドキュメントは、Julia標準ライブラリ `FileWatching` モジュールの機能設計を記述する。ファイル変更監視（watch_file / watch_folder）、ファイルディスクリプタポーリング（poll_fd）、およびPIDファイルロック管理（Pidfile）機能を提供するモジュールである。

### 本機能の処理概要

**業務上の目的・背景**：ファイルシステムの変更をリアルタイムに検知する仕組みは、ファイル同期、ビルドシステムの自動再ビルド、設定ファイルの動的リロード、プロセス間のロック制御など多くの場面で必要とされる。FileWatchingモジュールは、libuvのファイル監視機構を活用して、ファイルやフォルダの変更通知をJuliaの非同期タスクモデルと統合する。また、Pidfileサブモジュールにより、PIDファイルベースのプロセス間排他制御を提供する。

**機能の利用シーン**：ファイルの変更検知（watch_file）、ディレクトリの変更監視（watch_folder）、ファイルディスクリプタの読み書き可能状態のポーリング（poll_fd）、ファイルの存在を待機（poll_file）、プロセス間排他制御（mkpidlock / trymkpidlock）。

**主要な処理内容**：
1. watch_file: libuvのuv_fs_eventを使用してファイルの変更（変更・名前変更）を非同期に監視
2. watch_folder: ディレクトリ内の変更（ファイル追加・削除・変更）を監視
3. poll_fd: ファイルディスクリプタの読み書き可能状態をポーリング
4. poll_file: ファイルの存在や変更をポーリングベースで待機
5. Pidfile.mkpidlock: PIDファイルベースの排他ロックを作成
6. Pidfile.trymkpidlock: ロック失敗時にfalseを返す非ブロッキング版
7. FileMonitor / FolderMonitor: イベントベースの監視オブジェクト

**関連システム・外部連携**：libuv（ファイルシステムイベント）、OSのinotify（Linux）/FSEvents（macOS）/ReadDirectoryChanges（Windows）。

**権限による制御**：ファイルシステムのアクセス権限に依存。PIDファイルのロックはファイルの作成・書き込み権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | REPL | 参照画面 | watch_file/watch_folderの対話的実行 |

## 機能種別

ファイルシステム監視 / プロセス間制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | AbstractString | Yes | 監視対象のファイル/フォルダパス | 有効なパスであること |
| timeout_s | Real | No | タイムアウト秒数。デフォルト=-1（無制限） | 非負数または-1 |
| poll_interval | Real | No | ポーリング間隔（秒）。poll_file用 | 正の数 |
| at | String | Yes (Pidfile) | PIDファイルのパス | 有効なファイルパス |
| pid | Cint | No (Pidfile) | ロック対象のプロセスID | 有効なPID |
| stale_age | Real | No (Pidfile) | 古いPIDファイルの有効期限（秒） | 非負数 |
| wait | Bool | No (Pidfile) | ブロッキング待機するか | true/false |

### 入力データソース

- ファイルシステム（ファイル/ディレクトリパス）
- libuv（ファイルシステムイベント）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| FileEvent | 構造体 | ファイル変更イベント（changed, renamed, timedout フラグ） |
| (filename, event) | Tuple | watch_folder: 変更されたファイル名とイベント情報 |
| FDEvent | 構造体 | poll_fd: 読み書き可能状態（readable, writable, timedout） |
| LockMonitor | 構造体 | mkpidlock: ロックオブジェクト（close()で解放） |

### 出力先

- 戻り値として各種イベント構造体を返却
- PIDファイル（ファイルシステム上にロックファイルを作成）

## 処理フロー

### 処理シーケンス

```
1. watch_file(path) 呼び出し
   └─ FileMonitorオブジェクトを作成
   └─ libuv uv_fs_event_startでOSの監視を開始
   └─ イベント発生まで非同期に待機（Condition wait）
   └─ FileEventを返却

2. mkpidlock(at) 呼び出し
   └─ パスの正規化
   └─ open_exclusive()で排他的にファイルを開く
   └─ write_pidfile()でPID情報を書き込み
   └─ refreshタイマーを開始（stale防止）
   └─ LockMonitorオブジェクトを返却
   └─ close()時にファイルを削除
```

### フローチャート

```mermaid
flowchart TD
    A["watch_file(path)"] --> B["FileMonitor作成"]
    B --> C["uv_fs_event_start"]
    C --> D{"イベント発生?"}
    D -->|Yes| E["FileEvent構築"]
    D -->|timeout| F["timedout=true"]
    E --> G["返却"]
    F --> G

    H["mkpidlock(at)"] --> I["パス正規化"]
    I --> J["open_exclusive()"]
    J --> K{"ロック成功?"}
    K -->|Yes| L["write_pidfile()"]
    K -->|No, wait=true| M["FileMonitorで待機"]
    K -->|No, wait=false| N["エラー/false"]
    L --> O["refreshタイマー開始"]
    O --> P["LockMonitor返却"]
    M --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-116-01 | 非同期待機 | watch_file/watch_folderはJuliaの非同期タスクモデルで待機する | 監視時 |
| BR-116-02 | PIDファイルロック排他 | open_exclusive()でO_EXCLフラグを使用して排他的にファイルを作成 | mkpidlock使用時 |
| BR-116-03 | stale検出 | PIDファイルのmtimeがstale_ageを超え、かつPIDが無効な場合に古いロックを削除 | stale_age > 0の場合 |
| BR-116-04 | refreshタイマー | ロックのmtimeを定期更新してstale検出を防止 | refresh > 0の場合 |

### 計算ロジック

- stale判定: ファイルのmtime + stale_age < 現在時刻、かつプロセスが存在しない場合
- refresh間隔のデフォルト: stale_age / 2

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | タイムアウト | timeout_s以内にイベントが発生しない | timedout=trueのイベントが返される |
| IOError | ファイルアクセスエラー | 監視対象のファイル/ディレクトリが存在しない | 有効なパスを指定 |
| - | ロック失敗 | wait=falseでロックが取得できない場合 | trymkpidlockの場合falseが返る、mkpidlockの場合エラー |

### リトライ仕様

- mkpidlock(wait=true)の場合、FileMonitorでPIDファイルの削除を監視し、自動的にリトライ
- poll_interval設定によるポーリング間隔の制御

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

該当なし。PIDファイルロックは排他制御を提供するが、トランザクションではない。

## パフォーマンス要件

- watch_file/watch_folderはOSのネイティブファイルシステム通知を使用するため、ポーリングより効率的
- poll_fileは指定間隔でstatを呼び出すため、短い間隔ではCPU負荷が増加する

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

- PIDファイルはデフォルトでworld-readableモードで作成される（mode引数で制御可能）
- ファイル監視は指定パスのみを対象とし、再帰的な監視はwatch_folder単位

## 備考

- FileWatchingはJuliaの非同期I/Oモデル（libuv）に基づいている
- Pidfileサブモジュールは`mkpidlock`と`trymkpidlock`をエクスポート
- trymkpidlockはJulia 1.10で追加された

---

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

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

### 推奨読解順序

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

FileEvent、FDEvent、FileMonitor、FolderMonitor構造体を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | FileWatching.jl | `stdlib/FileWatching/src/FileWatching.jl` | FileEvent構造体（changed, renamed, timedoutフラグ）、FDEvent構造体（readable, writable, timedout） |

**読解のコツ**: FileWatchingはlibuv（C言語ライブラリ）のラッパーとして実装されている。ccallによるlibuv関数の呼び出しパターンを理解すると読みやすい。

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

watch_file, watch_folder, poll_fd, poll_fileがメインAPI。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | FileWatching.jl | `stdlib/FileWatching/src/FileWatching.jl` | watch_file関数: FileMonitorの作成とイベント待機のフロー |
| 2-2 | FileWatching.jl | `stdlib/FileWatching/src/FileWatching.jl` | watch_folder関数: FolderMonitorの作成とディレクトリ監視 |

#### Step 3: Pidfileサブモジュールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pidfile.jl | `stdlib/FileWatching/src/pidfile.jl` | **1-2行目**: Pidfileモジュール定義、mkpidlock/trymkpidlockのエクスポート |
| 3-2 | pidfile.jl | `stdlib/FileWatching/src/pidfile.jl` | **57-61行目**: LockMonitor構造体（path, fd, updateフィールド） |
| 3-3 | pidfile.jl | `stdlib/FileWatching/src/pidfile.jl` | **62-80行目**: mkpidlockの実装本体。open_exclusive → write_pidfile → Timer登録 |

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

```
watch_file(path, timeout_s)
    |
    +-- FileMonitor(path)
    |   +-- uv_fs_event_init()             # libuv初期化
    |   +-- uv_fs_event_start()            # 監視開始
    |
    +-- wait(monitor.notify)               # Condition待機
    |
    +-- FileEvent(changed, renamed)        # イベント構築

mkpidlock(at, pid)
    |
    +-- open_exclusive(at)
    |   +-- Base.Filesystem.open(O_CREAT|O_EXCL)
    |
    +-- write_pidfile(fd, pid)
    |
    +-- Timer(refresh_interval)            # mtime更新タイマー
    |
    +-- LockMonitor(at, fd, timer)
        +-- finalizer(close, lock)
```

### データフロー図

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

ファイルパス    ───▶  watch_file()              ───▶  FileEvent
                     libuv uv_fs_event              (changed/renamed/timedout)

ディレクトリ   ───▶  watch_folder()            ───▶  (filename, FileEvent)
                     libuv uv_fs_event

PIDファイルパス ───▶  mkpidlock()              ───▶  LockMonitor
                     open_exclusive + write_pidfile
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| FileWatching.jl | `stdlib/FileWatching/src/FileWatching.jl` | ソース | メインモジュール。watch_file, watch_folder, poll_fd, poll_file等 |
| pidfile.jl | `stdlib/FileWatching/src/pidfile.jl` | ソース | Pidfileサブモジュール。mkpidlock, trymkpidlock, LockMonitor |
| Project.toml | `stdlib/FileWatching/Project.toml` | 設定 | パッケージ依存関係定義 |
| runtests.jl | `stdlib/FileWatching/test/runtests.jl` | テスト | ユニットテスト |
