# 機能設計書 94-セーフポイント

## 概要

本ドキュメントは、Julia言語ランタイムにおけるセーフポイント（safepoint）機能の設計を記述する。セーフポイントは、GC（ガベージコレクション）のためのスレッド同期、スレッドの一時停止・再開、SIGINT配信の制御を行うための機構である。

### 本機能の処理概要

**業務上の目的・背景**：マルチスレッド環境でのGCでは、全てのミューテータスレッドを安全な状態（セーフポイント）で停止させる必要がある（Stop-the-World）。セーフポイント機構は、mprotectによるページ保護を利用して低オーバーヘッドでスレッドを同期させる。

**機能の利用シーン**：GC開始時の全スレッド停止（Stop-the-World）、GC終了後のスレッド再開、SIGINT（Ctrl+C）の安全な配信、スレッドの一時停止（デバッガ・プロファイラ向け）、外部スレッドのadopt時。

**主要な処理内容**：
1. セーフポイントページの初期化（`jl_safepoint_init`）- 4ページのメモリ領域確保
2. セーフポイントの有効化・無効化（`jl_safepoint_enable`/`jl_safepoint_disable`）- mprotectによるページ保護切替
3. GC同期（`jl_safepoint_start_gc`/`jl_safepoint_end_gc`）
4. GC待機（`jl_safepoint_wait_gc`/`jl_gc_wait_for_the_world`）
5. スレッド一時停止・再開（`jl_safepoint_suspend_thread`/`jl_safepoint_resume_thread`）
6. SIGINT制御（`jl_safepoint_enable_sigint`/`jl_safepoint_defer_sigint`/`jl_safepoint_consume_sigint`）

**関連システム・外部連携**：OSのmprotect/VirtualProtect機構、シグナル処理機構（SIGSEGVハンドラ）、スレッドスケジューラ。

**権限による制御**：スレッド0（メインスレッド）のセーフポイントポインタはページ1を指し、他スレッドはページ2を指す。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | ランタイム内部機能のため関連画面なし |

## 機能種別

ランタイムスレッド同期 / GC連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| idx | int | Yes | セーフポイントページインデックス（0-3） | 0 <= idx <= 3 |
| tid | int | Yes | 対象スレッドID | 有効なスレッドID |
| waitstate | int | Yes | 待機モード（0-3） | 0: 待機なし, 1: gc_state!=0, 2: GC非実行, 3: 完全停止 |

### 入力データソース

- GCサブシステムからのGC開始/終了通知
- シグナルハンドラからのSIGSEGV（セーフポイントページへのアクセス）
- デバッガ/プロファイラからのスレッド停止要求

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| GC実行権 | int | 1: GC実行権を取得, 0: 他スレッドがGC実行中 |
| suspend_count | int | スレッドの一時停止カウント |

### 出力先

スレッド状態の変更（gc_state, safepoint pointer）

## 処理フロー

### 処理シーケンス

```
1. 初期化（jl_safepoint_init）
   └─ 4ページのメモリ領域をmmap/VirtualAllocで確保（PAGE_READONLY）
2. GC開始（jl_safepoint_start_gc）
   ├─ jl_gc_runningを1に設定（CAS）
   ├─ ページ1,2をPROT_NONEに設定（jl_safepoint_enable）
   └─ 全スレッドがセーフポイントに到達するのを待機
3. 全スレッド到達待機（jl_gc_wait_for_the_world）
   └─ 各スレッドのgc_stateがUNSAFEでなくなるまで待機
4. GC終了（jl_safepoint_end_gc）
   ├─ ページ1,2をPROT_READに復元（jl_safepoint_disable）
   └─ jl_gc_runningを0に設定
5. スレッド一時停止（jl_safepoint_suspend_thread）
   ├─ suspend_countをインクリメント
   ├─ ページ3をPROT_NONEに設定
   └─ 対象スレッドのsafepointポインタをページ3に変更
```

### フローチャート

```mermaid
flowchart TD
    A[GC開始要求] --> B[jl_safepoint_start_gc]
    B --> C{jl_gc_running CAS成功?}
    C -->|Yes| D[jl_safepoint_enable ページ1,2]
    C -->|No| E[jl_safepoint_wait_gc]
    D --> F[jl_gc_wait_for_the_world]
    F --> G{全スレッドgc_state != UNSAFE?}
    G -->|No| H[uv_cond_wait]
    H --> G
    G -->|Yes| I[GC実行]
    I --> J[jl_safepoint_end_gc]
    J --> K[jl_safepoint_disable ページ1,2]
    K --> L[jl_gc_running = 0]
    L --> M[uv_cond_broadcast]
    E --> N[GC完了待ち]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-94-01 | 単一GCスレッド | 同時に1つのスレッドのみがGCを実行可能（CASで制御） | jl_safepoint_start_gc |
| BR-94-02 | ページ保護参照カウント | 各ページの有効化はカウンタで管理され、最大2回まで（ページ3は最大INT16_MAX） | jl_safepoint_enable |
| BR-94-03 | SIGINT段階制御 | SIGINTはページ0→ページ1の順に有効化し、ページ1→ページ0の順に無効化 | enable_sigint/defer_sigint/consume_sigint |
| BR-94-04 | スレッド停止4モード | waitstate 0-3の4段階の待機モードが存在 | jl_safepoint_suspend_thread |
| BR-94-05 | GC無効カウンタ | jl_gc_disable_counterが正の場合GCは実行されない | jl_safepoint_start_gc |

### 計算ロジック

- セーフポイントページアドレス: `jl_safepoint_pages + jl_page_size * idx`
- スレッド0のsafepointポインタ: `jl_safepoint_pages + jl_page_size`（ページ1）
- 他スレッドのsafepointポインタ: `jl_safepoint_pages + jl_page_size * 2 + sizeof(void*)`（ページ2 + オフセット）

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | メモリ確保失敗 | jl_safepoint_initでmmap/VirtualAlloc失敗 | stderrにエラー出力しabort() |
| - | タイムアウト | jl_gc_wait_for_the_worldでスレッドが到達しない | timeout_for_safepoint_straggler_sで指定秒数後にバックトレース出力 |

### リトライ仕様

セーフポイント到達待機はcondition variableで無限に待機するが、タイムアウトオプション（`--timeout-for-safepoint-straggler`）で未到達スレッドのバックトレースを出力可能。

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

safepoint_lockミューテックスで全操作がアトミックに保護される。

## パフォーマンス要件

- セーフポイントチェック自体は1回のメモリ読み取り（`*safepoint`）のみで、通常時のオーバーヘッドはほぼゼロ
- GC停止時はmprotectによるページフォルトでスレッドを停止

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

mprotectによるメモリ保護を利用するため、OSの仮想メモリ機構に依存する。

## 備考

- 4つのセーフポイントページの役割: ページ0=SIGINT、ページ1=GC（スレッド0）、ページ2=GC（他スレッド）、ページ3=スレッド一時停止
- macOS（Darwin）では`jl_mach_gc_end`が追加で呼び出される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | safepoint.c | `src/safepoint.c` | グローバル変数: jl_safepoint_pages, jl_safepoint_enable_cnt[4], safepoint_lock |

**読解のコツ**: 4つのセーフポイントページ（ページ0-3）の役割を理解することが最重要。各ページは`jl_page_size`バイトのサイズを持ち、mprotect/VirtualProtectで保護状態を切り替える。

#### Step 2: 初期化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | safepoint.c | `src/safepoint.c` | jl_safepoint_init（92行目）：4ページ分のメモリ確保 |

**主要処理フロー**:
1. **92-127行目**: `jl_safepoint_init` - mutex/cond初期化、4ページ分のmmap（PROT_READ）

#### Step 3: GC同期を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | safepoint.c | `src/safepoint.c` | jl_safepoint_start_gc（184行目）、jl_safepoint_end_gc（220行目） |
| 3-2 | safepoint.c | `src/safepoint.c` | jl_gc_wait_for_the_world（129行目） |

**主要処理フロー**:
- **184-218行目**: `jl_safepoint_start_gc` - CASでGC実行権取得、ページ1,2有効化
- **220-236行目**: `jl_safepoint_end_gc` - ページ1,2無効化、jl_gc_running=0
- **129-182行目**: `jl_gc_wait_for_the_world` - 全スレッドのgc_stateチェック（タイムアウト付き）

#### Step 4: スレッド一時停止を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | safepoint.c | `src/safepoint.c` | jl_safepoint_suspend_thread（328行目）、jl_safepoint_resume_thread（375行目） |

**主要処理フロー**:
- **328-371行目**: suspend_countインクリメント、ページ3有効化、safepointポインタ変更
- **375-407行目**: suspend_countデクリメント、ページ3無効化、safepointポインタ復元

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

```
jl_safepoint_init (safepoint.c:92)
    +-- uv_mutex_init(&safepoint_lock)
    +-- mmap(pgsz * 4, PROT_READ)

jl_safepoint_start_gc (safepoint.c:184)
    +-- jl_atomic_cmpswap(&jl_gc_running)
    +-- jl_safepoint_enable(1)
    +-- jl_safepoint_enable(2)

jl_gc_wait_for_the_world (safepoint.c:129)
    +-- jl_atomic_load(&ptls2->gc_state)
    +-- uv_cond_wait(&safepoint_cond_begin)
    +-- jl_try_record_thread_backtrace() [タイムアウト時]

jl_safepoint_end_gc (safepoint.c:220)
    +-- jl_safepoint_disable(2)
    +-- jl_safepoint_disable(1)
    +-- uv_cond_broadcast(&safepoint_cond_end)

jl_safepoint_suspend_thread (safepoint.c:328)
    +-- jl_safepoint_enable(3)
    +-- jl_wake_libuv()
```

### データフロー図

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

GC開始要求 ─────────────> jl_safepoint_start_gc ────────> ページ1,2: PROT_NONE
                                                          jl_gc_running = 1

スレッドSIGSEGV ────────> segv_handler ─────────────────> jl_set_gc_and_wait
                          (セーフポイントページ読取)         gc_state = WAITING

GC完了 ─────────────────> jl_safepoint_end_gc ─────────> ページ1,2: PROT_READ
                                                          jl_gc_running = 0
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| safepoint.c | `src/safepoint.c` | ソース | セーフポイント機構の中核実装 |
| signals-unix.c | `src/signals-unix.c` | ソース | SIGSEGVハンドラでセーフポイントを検出 |
| julia_threads.h | `src/julia_threads.h` | ヘッダ | jl_ptls_tのsafepoint関連フィールド |
| gc-common.h | `src/gc-common.h` | ヘッダ | GC関連の定義 |
| julia_internal.h | `src/julia_internal.h` | ヘッダ | セーフポイント関数の宣言 |
