# 通知設計書 12-DirectoryWatcher WatcherGotInvalidated

## 概要

本ドキュメントは、clangのDirectoryWatcherコンポーネントにおける`WatcherGotInvalidated`イベント通知の設計仕様を記述する。DirectoryWatcherインスタンスが無効化され、その動作が未定義になったことをクライアントに通知するイベントメカニズムである。

### 本通知の処理概要

DirectoryWatcher WatcherGotInvalidatedは、ファイルシステム監視機能においてWatcherインスタンスが何らかの理由で無効になった際に発生する重要な通知イベントである。これは、正常終了時にも異常終了時にも送信される終端イベントである。

**業務上の目的・背景**：ファイルシステム監視には、カーネルリソースの制限（inotifyのウォッチディスクリプタ上限、FSEventsのイベントドロップなど）が存在する。これらの制限を超過した場合や、監視対象ディレクトリが削除された場合、Watcherは正常に機能しなくなる。WatcherGotInvalidatedは、この状態をクライアントに通知し、適切なリカバリー処理（Watcherの再作成など）を促すために必要である。

**通知の送信タイミング**：以下の状況で送信される。(1)監視対象ディレクトリが削除された後（WatchedDirRemovedに続いて）、(2)カーネルがリソース制限超過を通知した場合、(3)DirectoryWatcherインスタンスのデストラクタが呼び出された時、(4)内部エラーが発生した場合。

**通知の受信者**：DirectoryWatcherインスタンスを作成した際に登録されたReceiverコールバック関数が受信者となる。Watcherのライフタイム中に少なくとも1回は必ず受信することが保証される。

**通知内容の概要**：`DirectoryWatcher::Event`構造体として通知される。`EventKind`は`WatcherGotInvalidated`、`Filename`は空文字列である。この通知を受信した後のWatcherの動作は未定義である。

**期待されるアクション**：受信者はこの通知を受け取った後、現在のDirectoryWatcherインスタンスを破棄し、必要に応じて新しいインスタンスを作成する必要がある。この通知後は一部のイベントが失われている可能性があるため、状態の再同期が推奨される。

## 通知種別

イベントコールバック（C++関数オブジェクト）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（イベントキュー経由、またはデストラクタで同期的） |
| 優先度 | 最高（終端イベント） |
| リトライ | なし（一度限りのイベント） |

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

DirectoryWatcher::create()で登録されたReceiverコールバック関数に対して通知される。デストラクタでの通知はdispatch_sync（macOS）または同期的なキュー操作（Linux）で実行され、確実に受信者に届く。

## 通知テンプレート

### イベント通知の場合

| 項目 | 内容 |
|-----|------|
| イベント種別 | `DirectoryWatcher::Event::EventKind::WatcherGotInvalidated` |
| Filename | 空文字列 `""` |
| IsInitial | `false`（実行時イベント） |

### 本文テンプレート

```cpp
DirectoryWatcher::Event{
    DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
    ""  // Filename is empty for invalidation events
}
```

### 添付ファイル

該当なし（プログラムイベントのため）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| Kind | イベント種別 | `EventKind::WatcherGotInvalidated`固定 | Yes |
| Filename | ファイル名 | 空文字列固定 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| リソース制限 | kFSEventStreamEventFlagUserDropped | macOSでイベントがドロップされた | ユーザー空間でイベントがドロップされた |
| リソース制限 | kFSEventStreamEventFlagKernelDropped | macOSでカーネルがイベントをドロップ | カーネル空間でイベントがドロップされた |
| リソース制限 | kFSEventStreamEventFlagMustScanSubDirs | macOSでサブディレクトリスキャンが必要 | イベント結合によりスキャンが必要 |
| ディレクトリ削除 | IN_DELETE_SELF / IN_MOVE_SELF | WatchedDirRemovedの後に送信 | 監視対象ディレクトリが削除された |
| デストラクタ | DirectoryWatcher破棄 | 常に送信 | インスタンス破棄時に必ず送信 |
| 内部エラー | 不明なイベントタイプ | 常に送信 | 想定外のイベント受信時 |
| OSイベント（Linux） | IN_IGNORED | Watchが解除された | カーネルがウォッチを解除した |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 既に送信済み | WatcherGotInvalidatedは1インスタンスに対して1回のみ送信（デストラクタ以外の場合でも） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[トリガー発生] --> B{トリガー種別}
    B -->|リソース制限| C[StreamInvalidatingFlags検出]
    B -->|ディレクトリ削除| D[WatchedDirRemoved後]
    B -->|デストラクタ| E[~DirectoryWatcher]
    B -->|内部エラー| F[エラー検出]
    C --> G[WatcherGotInvalidatedイベント生成]
    D --> G
    E --> G
    F --> G
    G --> H[Receiverコールバック実行]
    H --> I[StopWork / 処理終了]
    I --> J[Watcher無効化完了]
```

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

### 参照テーブル一覧

該当なし（ファイルシステム監視のためDBは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| カーネルイベントドロップ | リソース不足やシステム負荷 | WatcherGotInvalidatedを送信し、クライアントに再作成を促す |
| 不明イベント | 想定外のイベントタイプ受信 | llvm_unreachable呼び出し前にWatcherGotInvalidatedを送信 |
| epoll/inotifyエラー | システムコール失敗 | StopWorkを呼び出してWatcherを無効化 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回（リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | なし（再作成が必要） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 1回（終端イベント） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

24時間常時（イベント発生時またはデストラクタ呼び出し時に即時送信）

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

- カーネルリソース制限によるDoS攻撃の可能性を考慮
- Watcher再作成時の無限ループ防止が必要
- デストラクタでの送信はdispatch_sync（macOS）で同期的に実行されるため、デッドロックに注意

## 備考

- WatcherGotInvalidatedはWatcherのライフタイム中に必ず1回は受信することが保証される
- デストラクタが呼び出される際、まだWatcherGotInvalidatedが送信されていなければ送信される
- macOS実装では、StreamInvalidatingFlagsマスク（UserDropped|KernelDropped|MustScanSubDirs）でチェック
- Linux実装では、StopWork()メソッドがキューにWatcherGotInvalidatedをプッシュ
- この通知後もWatcherから一部のイベントが届く可能性があるが、動作は未定義

---

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

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

### 推奨読解順序

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

まず、WatcherGotInvalidatedの意味と発生条件を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DirectoryWatcher.h | `clang/include/clang/DirectoryWatcher/DirectoryWatcher.h` | WatcherGotInvalidatedの定義と詳細なコメント（行73-90）を確認 |

**読解のコツ**: 行73-90のコメントには、WatcherGotInvalidatedが発生する条件（リソース制限、ディレクトリ削除後、デストラクタ）が詳細に説明されている。特に「少なくとも1回は受信することが保証される」という点が重要。

#### Step 2: Linux実装のStopWork()を理解する

Linux実装でWatcherGotInvalidatedがどのように生成されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DirectoryWatcher-linux.cpp | `clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp` | StopWork()メソッド（行164-168）でWatcherGotInvalidatedをキューにプッシュ |

**主要処理フロー**:
1. **行164-168**: StopWork()がWatcherGotInvalidatedをEventQueueにプッシュ
2. **行167**: InotifyPollingStopSignal.signal()でポーリングスレッドに停止を通知
3. **行258-261**: IN_DELETE_SELF/IN_MOVE_SELF後にStopWork()を呼び出し
4. **行263-265**: IN_IGNORED（ウォッチ解除）後にStopWork()を呼び出し

#### Step 3: macOS実装の無効化処理を理解する

macOS実装での無効化トリガーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DirectoryWatcher-mac.cpp | `clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp` | StreamInvalidatingFlags（行97-99）とeventStreamCallback内の処理（行117-120, 150-152） |

**主要処理フロー**:
- **行97-99**: StreamInvalidatingFlagsマスク定義（UserDropped|KernelDropped|MustScanSubDirs）
- **行117-120**: StreamInvalidatingFlagsがセットされていればWatcherGotInvalidatedを生成してbreak
- **行128-131**: ディレクトリ削除後にWatcherGotInvalidatedを追加
- **行150-152**: 不明なイベントタイプでもWatcherGotInvalidatedを生成

#### Step 4: デストラクタでの保証を確認する

デストラクタでWatcherGotInvalidatedが確実に送信されることを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DirectoryWatcher-mac.cpp | `clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp` | デストラクタ（行56-71）でdispatch_syncを使用して同期的にReceiverを呼び出し |
| 4-2 | DirectoryWatcher-linux.cpp | `clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp` | デストラクタ（行120-126）でStopWork()を呼び出し、スレッドのjoinを待機 |

**主要処理フロー（macOS）**:
- **行60-67**: dispatch_syncブロック内でstopFSEventStreamとReceiver呼び出しを実行

**主要処理フロー（Linux）**:
- **行121**: StopWork()呼び出しでWatcherGotInvalidatedをキューにプッシュ
- **行122-123**: InotifyPollingThreadとEventsReceivingThreadをjoin

#### Step 5: テストコードで動作を確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | DirectoryWatcherTest.cpp | `clang/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp` | InvalidatedWatcherテスト（行442-461）とInvalidatedWatcherAsyncテスト（行463-500） |

**主要処理フロー**:
- **行445-446**: 期待されるイベントとしてWatcherGotInvalidatedを設定
- **行448-458**: スコープ終了でDWが破棄され、WatcherGotInvalidatedが送信されることを確認
- **行471-478**: 非同期モードでもWatcherGotInvalidatedが正しく送信されることを確認

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

```
DirectoryWatcher
    │
    ├─ [通常動作中の無効化]
    │      │
    │      ├─ [Linux] InotifyPollingLoop
    │      │      ├─ IN_DELETE_SELF / IN_MOVE_SELF → StopWork()
    │      │      ├─ IN_IGNORED → StopWork()
    │      │      └─ epoll/inotifyエラー → StopWork()
    │      │             └─ Queue.push_back(WatcherGotInvalidated)
    │      │
    │      └─ [macOS] eventStreamCallback
    │             ├─ StreamInvalidatingFlags → WatcherGotInvalidated
    │             └─ Unknown event → WatcherGotInvalidated
    │
    └─ [デストラクタ]
           │
           ├─ [Linux] ~DirectoryWatcherLinux
           │      └─ StopWork()
           │             └─ Queue.push_back(WatcherGotInvalidated)
           │
           └─ [macOS] ~DirectoryWatcherMac
                  └─ dispatch_sync(^{ Receiver(WatcherGotInvalidated) })
```

### データフロー図

```
[トリガー]                [処理]                          [出力]

リソース制限 ─────────┐
                     │
ディレクトリ削除 ────┼──▶ StopWork() / dispatch_sync ──▶ Receiver()
                     │         │
デストラクタ ────────┤         ▼
                     │   WatcherGotInvalidated
内部エラー ──────────┘

※ この通知後のWatcher動作は未定義
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DirectoryWatcher.h | `clang/include/clang/DirectoryWatcher/DirectoryWatcher.h` | ヘッダー | Event構造体・WatcherGotInvalidated定義（行73-90） |
| DirectoryWatcher-linux.cpp | `clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp` | ソース | Linux実装のStopWork()（行164-168）、デストラクタ（行120-126） |
| DirectoryWatcher-mac.cpp | `clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp` | ソース | macOS実装のStreamInvalidatingFlags（行97-99）、デストラクタ（行56-71） |
| DirectoryWatcherTest.cpp | `clang/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp` | テスト | InvalidatedWatcherテスト（行442-500） |
