# 通知設計書 7-GlobalOperationStopped

## 概要

本ドキュメントは、Roslynプロジェクトにおける`GlobalOperationStopped`イベント通知の設計仕様を記載するものである。このイベントは、ビルドなどのグローバル操作が終了した際に発火するイベント通知であり、一時停止していたバックグラウンド処理の再開トリガーとして活用される。

### 本通知の処理概要

`GlobalOperationStopped`は、`IGlobalOperationNotificationService`インターフェースで定義された`Stopped`イベントであり、グローバル操作の終了時にサブスクライバへ通知を送信する。`Started`イベントとペアで動作し、すべてのグローバル操作が完了した時点でイベントが発火する。

**業務上の目的・背景**：グローバル操作（ビルド、デバッグ、Gitブランチ切り替えなど）が完了した後、一時停止していたバックグラウンド処理を再開する必要がある。このイベント通知により、ソリューションクローラーやその他のバックグラウンドサービスは通常の動作を再開し、コード分析やインデックス更新などの処理を継続できる。`Started`イベントで停止し、`Stopped`イベントで再開するという対称的な設計により、リソース管理が適切に行われる。

**通知の送信タイミング**：`GlobalOperationRegistration.Dispose()`が呼び出され、すべてのアクティブなグローバル操作が終了した時点で発火する。複数の操作が並行して実行されている場合、最後の操作が終了した時点でのみイベントが発火する（参照カウント方式）。

**通知の受信者**：`Stopped`イベントをサブスクライブしているコンポーネント。具体的には、SolutionCrawler、プリエンプティブなリモートホスト同期、その他のバックグラウンドアイドルプロセッサなど。

**通知内容の概要**：`EventArgs.Empty`が送信される。通知自体には詳細な情報は含まれず、「すべてのグローバル操作が終了した」というシグナルのみを伝達する。

**期待されるアクション**：イベントをサブスクライブしているコンポーネントは、`Started`イベントで停止していたバックグラウンド処理を再開する。

## 通知種別

アプリ内イベント通知（EventHandler）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期イベント発火（AsyncBatchingWorkQueueで遅延処理） |
| 優先度 | 高（バックグラウンド処理再開のため） |
| リトライ | 無し（イベント通知のため） |

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

`Stopped`イベントにサブスクライブしているすべてのハンドラに対してイベントが発火される。サブスクライバの登録・解除はイベントの標準的な`+=`/`-=`演算子で行う。

## 通知テンプレート

### イベント定義

```csharp
public event EventHandler Stopped;
```

### イベント引数

| 項目 | 内容 |
|-----|------|
| sender | IGlobalOperationNotificationServiceインスタンス |
| args | EventArgs.Empty |

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | - | - | イベント通知のため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| N/A | - | イベント引数はEventArgs.Empty | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システム処理 | GlobalOperationRegistration.Dispose() | 最後の操作終了時 | グローバル操作登録のDispose呼び出し |
| システム処理 | BulkFileOperation.End | End イベント発火時 | Gitブランチ切り替え等のファイル一括操作終了 |
| システム処理 | ビルド終了 | ビルドプロセス終了時 | ソリューション/プロジェクトのビルド終了 |
| システム処理 | ソリューション変更完了 | SolutionEventMonitor検知時 | ソリューションの変更操作完了 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| まだ他のグローバル操作が実行中の場合 | 参照カウントが0より大きい場合、Stoppedイベントは発火しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[GlobalOperationRegistration.Dispose が呼び出される] --> B[service.Done this が呼び出される]
    B --> C[ロックを取得]
    C --> D[registrationsから削除]
    D --> E{registrations.Count == 0?}
    E -->|Yes| F[operations.Clear]
    F --> G[EventQueueにfalse Added]
    E -->|No| H[処理終了]
    G --> I[ProcessEventsAsyncが実行]
    I --> J[Stopped イベントを発火]
    J --> H
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | - | データベースアクセスなし（インメモリ管理） |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| N/A | - | データベースへの更新なし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| イベントハンドラ例外 | サブスクライバが例外をスローした場合 | 例外はキャッチされず伝播する可能性あり |
| 二重Dispose | 同じRegistrationが複数回Disposeされた場合 | Contract.ThrowIfFalseで例外 |
| シャットダウン中 | 環境シャットダウン中にイベント発火 | ファイナライザでの検証をスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（グローバル操作の頻度に依存） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

グローバル操作の終了に応じて即座に発火されるため、時間帯制限なし。

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

- イベントハンドラは信頼されたコード内でのみサブスクライブされる
- イベント引数には機密情報は含まれない（EventArgs.Empty）
- イベントハンドラの実行は同期的であり、長時間実行するハンドラはパフォーマンスに影響する可能性がある

## 備考

- `Started`イベントと対になるイベントであり、必ずペアで使用される
- `AbstractGlobalOperationNotificationService`が抽象基底クラスとして実装を提供
- 参照カウント方式により、ネストされたグローバル操作を適切に管理
- `AsyncBatchingWorkQueue`を使用してイベント発火を最適化
- `_operations.Clear()`により、すべての操作名がクリアされる

---

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

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

### 推奨読解順序

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

まず、インターフェース定義とイベントの構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | IGlobalOperationNotificationService.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/Notification/IGlobalOperationNotificationService.cs` | インターフェース定義、Stoppedイベント（行22-23）を理解する |

**読解のコツ**: `Started`と`Stopped`がペアで動作することに注目。

#### Step 2: 操作終了処理を理解する

DisposeとDoneメソッドの実装詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AbstractGlobalOperationNotificationService.GlobalOperationRegistration.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/Notification/AbstractGlobalOperationNotificationService.GlobalOperationRegistration.cs` | Disposeメソッドの実装（行13-16）、service.Done(this)の呼び出しを理解する |
| 2-2 | AbstractGlobalOperationNotificationService.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/Notification/AbstractGlobalOperationNotificationService.cs` | Doneメソッドの実装（行81-94）、参照カウント減算と操作クリアを理解する |

**主要処理フロー**:
1. **行81-94（Done）**: ロックを取得し、registrationsから削除
2. **行88-91**: registrations.Count == 0の場合、operationsをクリアしてEventQueueにfalseを追加
3. **行48-57（ProcessEventsAsync）**: キューから取り出してStopped イベントを発火（valueがfalseの場合）

#### Step 3: Visual Studio実装を理解する

BulkFileOperation終了時の動作を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | VisualStudioGlobalOperationNotificationService.cs | `src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs` | StopBulkFileOperationNotificationメソッド（行63-70）、localRegistration?.Dispose()を理解する |

#### Step 4: サブスクライバを理解する

イベントをサブスクライブしている箇所を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | UnitTestingGlobalOperationAwareIdleProcessor.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingGlobalOperationAwareIdleProcessor.cs` | Stoppedイベント受信時の処理再開ロジックを理解する |

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

```
グローバル操作の終了
    │
    └─ GlobalOperationRegistration.Dispose()
           │
           └─ AbstractGlobalOperationNotificationService.Done(registration)
                  │
                  ├─ lock(_gate) で排他制御
                  │
                  ├─ _registrations.Remove(registration)
                  │
                  └─ if (Count == 0)
                         │
                         ├─ _operations.Clear()
                         │
                         └─ _eventQueue.AddWork(false)
                                │
                                └─ ProcessEventsAsync
                                       │
                                       └─ Stopped?.Invoke(this, EventArgs.Empty)
                                              │
                                              └─ サブスクライバがバックグラウンド処理を再開
```

### データフロー図

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

Registration.Dispose() ─────▶ Done(registration)
                                │
                                ▼
                         参照カウント減算
                                │
                                ▼
                      ┌─ Count == 0? ─┐
                      │               │
                     Yes             No
                      │               │
                      ▼               │
               operations.Clear()     │
                      │               │
                      ▼               │
               EventQueue.AddWork(false)
                      │               │
                      ▼               │
               ProcessEventsAsync     │
                      │               │
                      ▼               │
               Stopped イベント発火 ──┴──▶ サブスクライバへ通知
                                              │
                                              ▼
                                       バックグラウンド処理再開
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| IGlobalOperationNotificationService.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/Notification/IGlobalOperationNotificationService.cs` | インターフェース | サービスの契約定義 |
| AbstractGlobalOperationNotificationService.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/Notification/AbstractGlobalOperationNotificationService.cs` | ソース | 抽象基底クラス実装（Done メソッド） |
| AbstractGlobalOperationNotificationService.GlobalOperationRegistration.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/Notification/AbstractGlobalOperationNotificationService.GlobalOperationRegistration.cs` | ソース | 操作登録クラス（Dispose） |
| VisualStudioGlobalOperationNotificationService.cs | `src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs` | ソース | VS環境の具体実装 |
| UnitTestingGlobalOperationAwareIdleProcessor.cs | `src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingGlobalOperationAwareIdleProcessor.cs` | ソース | サブスクライバ例 |
