# 機能設計書 78-ビルドグラフ

## 概要

本ドキュメントは、F#コンパイラにおける並列ビルドの依存関係グラフ管理機能の設計を記述する。GraphNodeは、非同期計算の結果をキャッシュし、複数のリクエスト間で共有するためのプリミティブを提供する。

### 本機能の処理概要

**業務上の目的・背景**：F#コンパイラは、複数のソースファイルを並列に型チェックすることでコンパイル時間を短縮する。しかし、ファイル間には依存関係が存在し、あるファイルの型チェック結果が別のファイルで必要になる場合がある。GraphNodeは、計算結果の遅延評価とキャッシング、および複数の並行リクエストに対する排他制御を提供することで、効率的な並列ビルドを実現する。

**機能の利用シーン**：IDE統合でのインクリメンタルビルド、並列型チェック、依存ファイルの計算結果の共有など、非同期計算の結果を複数のコンシューマーで共有する必要があるすべての場面で使用される。

**主要な処理内容**：
1. 非同期計算（Async<'T>）のラッピング
2. SemaphoreSlimによる計算の排他制御
3. 計算結果のキャッシング
4. UIカルチャの伝播（Visual Studio統合用）
5. 複数リクエストの効率的なハンドリング

**関連システム・外部連携**：System.Threading（SemaphoreSlim、Interlocked）、System.Globalization（CultureInfo）と連携する。

**権限による制御**：本機能に特別な権限制御は存在しない。

## 関連画面

本機能はコンパイラ内部機能であり、直接関連する画面は存在しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

非同期計算 / キャッシュ / 並行制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| computation | Async<'T> | Yes | キャッシュ対象の非同期計算 | null不可 |
| result | 'T | No | 事前計算済みの結果（FromResult用） | - |
| preferredUiLang | string option | No | 優先UIカルチャ | 有効なカルチャ名 |

### 入力データソース

- 非同期計算の定義

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| GetOrComputeValue結果 | Async<'T> | キャッシュされた/計算された値を返す非同期計算 |
| TryPeekValue結果 | ValueOption<'T> | キャッシュされた値（あれば） |
| HasValue | bool | 結果がキャッシュ済みか |
| IsComputing | bool | 現在計算中か |

### 出力先

- 呼び出し元への返却値

## 処理フロー

### 処理シーケンス

```
【GetOrComputeValue】
1. 高速パス: キャッシュ済みならcachedResultNodeを即返却
2. 低速パス:
   a. キャンセルトークン取得
   b. requestCountをインクリメント
   c. セマフォ取得を待機
   d. 再度キャッシュ確認（ダブルチェック）
   e. UIカルチャ設定
   f. 計算実行
   g. 結果をキャッシュ
   h. セマフォ解放
   i. requestCountをデクリメント
3. 結果返却
```

### フローチャート

```mermaid
flowchart TD
    A[GetOrComputeValue呼び出し] --> B{cachedResultNodeあり?}
    B -->|Yes| C[cachedResultNode返却]
    B -->|No| D[CancellationToken取得]
    D --> E[requestCount++]
    E --> F[semaphore.WaitAsync]
    F --> G{cachedResult.IsSome?}
    G -->|Yes| H[cachedResult返却]
    G -->|No| I[CurrentUICulture設定]
    I --> J[computation実行]
    J --> K[結果をcachedResultに格納]
    K --> L[cachedResultNode作成]
    L --> M[computationをクリア]
    M --> N[結果返却]
    H --> O[finally: semaphore.Release]
    N --> O
    O --> P[requestCount--]
    P --> Q[完了]
    C --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-78-01 | 一度だけ計算 | 計算は最初のリクエストでのみ実行される | 常時 |
| BR-78-02 | 結果キャッシュ | 計算結果は永続的にキャッシュされる | 計算完了後 |
| BR-78-03 | セマフォ排他 | 複数リクエストはセマフォで直列化される | 計算未完了時 |
| BR-78-04 | カルチャ伝播 | VS UIスレッドのカルチャを計算スレッドに伝播 | 計算実行時 |
| BR-78-05 | 計算クリア | 計算完了後、元のcomputation参照をクリア | 計算完了後 |

### 計算ロジック

- 高速パス判定: `not (obj.ReferenceEquals(cachedResultNode, null))`
- IsComputing判定: `requestCount > 0`

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| OperationCanceledException | 例外 | キャンセルトークンがキャンセル | セマフォ解放をスキップ |

### リトライ仕様

キャンセル時はfinallyブロックでセマフォ解放を試みる。Wait()が例外を投げた場合はリリースをスキップ。

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

本機能はトランザクション管理を必要としない。SemaphoreSlimによる排他制御のみ。

## パフォーマンス要件

- キャッシュ済みの場合はO(1)で返却（高速パス）
- セマフォ取得は非同期で待機（スレッドブロックなし）
- 計算完了後は元のcomputationをnull化してメモリ解放

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

- カルチャ設定は信頼されたコンテキストでのみ使用
- 計算内容はコンパイラ内部データのみ

## 備考

- GraphNode.cultureはモジュールレベルのmutable変数で、VS UIスレッドから設定される
- FromResultで事前計算済みの結果をGraphNodeにラップ可能
- requestCountはデバッグ/監視用途（IsComputing判定）

---

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

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

### 推奨読解順序

#### Step 1: モジュール構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | BuildGraph.fs | `src/Compiler/Facilities/BuildGraph.fs` | GraphNodeモジュールとクラス |

**主要処理フロー**:
- **9-20行目**: GraphNodeモジュール - カルチャ管理
- **22-81行目**: GraphNode<'T>クラス - 非同期計算のキャッシュ

#### Step 2: 状態管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | BuildGraph.fs | `src/Compiler/Facilities/BuildGraph.fs` | フィールド定義とコンストラクタ |

**主要処理フロー**:
- **23行目**: プライベートコンストラクタ - 3つのパラメータ
- **25-29行目**: mutableフィールド - computation、cachedResult、cachedResultNode
- **34行目**: semaphore - SemaphoreSlim(1, 1)
- **80行目**: パブリックコンストラクタ - computationのみ

**読解のコツ**: `cachedResultNode`がnullでないことが「キャッシュ済み」のマーカーとなる。ValueOption<'T>のcachedResultとは別に、Async<'T>のcachedResultNodeを保持することで、高速パスでボクシングを回避。

#### Step 3: 計算フローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | BuildGraph.fs | `src/Compiler/Facilities/BuildGraph.fs` | GetOrComputeValue |

**主要処理フロー**:
- **36-68行目**: GetOrComputeValue - メインロジック
- **38-39行目**: 高速パス - キャッシュ済みなら即返却
- **42行目**: CancellationToken取得
- **44行目**: semaphore.WaitAsync - 非同期待機
- **49-50行目**: ダブルチェック - セマフォ取得後の再確認
- **52行目**: カルチャ設定
- **53-57行目**: 計算実行とキャッシュ
- **61-65行目**: finally - セマフォ解放（例外処理付き）

**読解のコツ**: finallyブロックでenter.Wait()を呼ぶのは、WaitAsyncがキャンセルされた場合にセマフォを取得していないことを確認するため。

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

```
呼び出し元（TypeChecker等）
    │
    └─ GraphNode<'T>.GetOrComputeValue()
           │
           ├─ 高速パス: cachedResultNode返却
           │
           └─ 低速パス
                  │
                  ├─ Async.CancellationToken
                  │
                  ├─ Interlocked.Increment(&requestCount)
                  │
                  ├─ semaphore.WaitAsync(ct)
                  │
                  ├─ cachedResult チェック（ダブルチェック）
                  │
                  ├─ Thread.CurrentThread.CurrentUICulture設定
                  │
                  ├─ computation実行（!演算子）
                  │
                  ├─ キャッシュ格納
                  │      ├─ cachedResult <- ValueSome result
                  │      ├─ cachedResultNode <- async.Return result
                  │      └─ computation <- null
                  │
                  └─ finally
                         ├─ semaphore.Release()
                         └─ Interlocked.Decrement(&requestCount)
```

### データフロー図

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

Async<'T> ──────────────▶ GraphNode作成 ─────────────────────▶ GraphNode<'T>
(computation)                 │
                              ├─ computation保存
                              │
                              └─ cachedResult = ValueNone

GraphNode<'T> ──────────▶ GetOrComputeValue ─────────────────▶ Async<'T>
                              │
                              ├─ キャッシュチェック
                              │      │
                              │      ├─ Hit → cachedResultNode
                              │      │
                              │      └─ Miss → computation実行
                              │               │
                              │               └─ 結果をキャッシュ
                              │
                              └─ 結果返却

GraphNode<'T> ──────────▶ TryPeekValue ──────────────────────▶ ValueOption<'T>
                              │
                              └─ cachedResult返却（計算なし）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| BuildGraph.fs | `src/Compiler/Facilities/BuildGraph.fs` | ソース | GraphNode実装 |
| BuildGraph.fsi | `src/Compiler/Facilities/BuildGraph.fsi` | シグネチャ | 公開APIの定義 |
| IncrementalBuild.fs | `src/Compiler/Service/IncrementalBuild.fs` | ソース | GraphNodeの使用例（並列ビルド） |
| BackgroundCompiler.fs | `src/Compiler/Service/BackgroundCompiler.fs` | ソース | GraphNodeの使用例（バックグラウンドコンパイル） |
