# 機能設計書 92-シグナル処理

## 概要

本ドキュメントは、Julia言語ランタイムにおけるシグナル処理機能の設計を記述する。Unixシグナル（SIGSEGV, SIGINT, SIGFPE等）の受信、ハンドリング、およびJulia例外への変換を担うプラットフォーム固有のランタイム機能である。

### 本機能の処理概要

**業務上の目的・背景**：Juliaランタイムはネイティブコードを実行するため、OSシグナル（セグメンテーション違反、浮動小数点例外、割り込みなど）を適切に処理し、Julia例外として安全にユーザコードに伝達する必要がある。また、GCセーフポイントの実現にもシグナル機構が利用される。

**機能の利用シーン**：スタックオーバーフロー検出、Ctrl+C（SIGINT）による実行中断、ゼロ除算例外、GCのためのスレッド停止（SIGUSR2）、プロファイリングのためのタイマーシグナル、致命的エラー時のバックトレース取得など。

**主要な処理内容**：
1. シグナルハンドラの登録（`jl_install_default_signal_handlers`）
2. SIGSEGV/SIGBUS処理（`segv_handler`）- セーフポイント、スタックオーバーフロー、読み取り専用メモリ書き込みの判定
3. SIGUSR2によるスレッド一時停止・状態取得（`usr2_handler`）
4. SIGINT配信（`jl_try_deliver_sigint`）
5. プロファイリング用タイマーシグナル（SIGUSR1）
6. シグナルリスナースレッドによる集約処理（`signal_listener`）
7. メモリバリア実装の選択（sys_membarrier / mprotect / thread_suspend）

**関連システム・外部連携**：OS固有シグナル機構（POSIX signals、Mach exceptions on macOS）、libuvイベントループ、GCセーフポイント機構。

**権限による制御**：スレッド0（メインスレッド）のみがSIGINTによるInterruptException送出の対象となる。

## 関連画面

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

## 機能種別

ランタイムシグナル管理 / 例外変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| sig | int | Yes | シグナル番号 | 有効なシグナル番号 |
| info | siginfo_t* | Yes | シグナル情報構造体 | OS提供 |
| context | void* (ucontext_t*) | Yes | シグナル発生時のCPUコンテキスト | OS提供 |

### 入力データソース

OSカーネルからのシグナル配信

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Julia例外 | jl_value_t* | jl_interrupt_exception, jl_stackovf_exception, jl_diverror_exception等 |
| バックトレース | jl_bt_element_t[] | シグナル発生時のスタックトレース |

### 出力先

Julia例外ハンドラ（`ct->eh`）へのlongjmpまたはプロセス終了

## 処理フロー

### 処理シーケンス

```
1. シグナルハンドラ登録（jl_install_default_signal_handlers）
   ├─ SIGFPE -> fpe_handler
   ├─ SIGSEGV/SIGBUS -> segv_handler
   ├─ SIGUSR2 -> usr2_handler
   ├─ SIGILL/SIGABRT/SIGSYS -> sigdie_handler
   └─ SIGINT -> sigint_handler
2. シグナルリスナースレッド起動（signal_listener）
   └─ sigwait/keventでSIGINT, SIGTERM等を待機
3. シグナル受信時の処理分岐
   ├─ SIGSEGV: セーフポイントチェック、スタックオーバーフロー、読取専用メモリ
   ├─ SIGFPE: jl_diverror_exceptionをスロー
   ├─ SIGINT: jl_try_deliver_sigintでスレッド0に配信
   ├─ SIGUSR2: スレッド停止/プロファイル/終了要求
   └─ SIGTERM等: 致命的シグナルとして終了処理
```

### フローチャート

```mermaid
flowchart TD
    A[シグナル受信] --> B{シグナル種別}
    B -->|SIGSEGV| C{si_code == SEGV_ACCERR}
    C -->|セーフポイントアドレス| D[jl_set_gc_and_wait]
    C -->|スタック上| E[StackOverflowError]
    C -->|読取専用メモリ| F[ReadOnlyMemoryError]
    C -->|その他| G[sigdie_handler - プロセス終了]
    B -->|SIGFPE| H[DivideError]
    B -->|SIGINT| I[signal_listener]
    I --> J[jl_try_deliver_sigint]
    J --> K[SIGUSR2でスレッド0に配信]
    B -->|SIGUSR2| L{request種別}
    L -->|1| M[状態取得・待機]
    L -->|2| N[SIGINT配信]
    L -->|3| O[終了処理]
    L -->|4| P[no-op]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-92-01 | セーフポイント検出 | SIGSEGVがセーフポイントページへのアクセス（読み取り違反）の場合、GC待機に入る | SEGV_ACCERRかつセーフポイントアドレスかつ書き込みフォルトでない |
| BR-92-02 | SIGINT配信制限 | ワーカースレッド（tid!=0）ではSIGINTを無視する | セーフポイントからの復帰時 |
| BR-92-03 | 致命シグナル段階的終了 | 複数回の致命シグナルで段階的に強制終了する | thread0_exit_count > 1 |
| BR-92-04 | メモリバリア実装選択 | sys_membarrier > mprotect > thread_suspend の優先順で選択 | プラットフォーム能力に応じて |

### 計算ロジック

- `is_write_fault`: CPUアーキテクチャ固有のレジスタフラグからメモリ書き込み違反かを判定
- `timer_graceperiod_elapsed`: タイマー削除後2秒間の猶予期間を`jl_hrtime()`で判定

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | StackOverflowError | SIGSEGVでスタック上のアドレスへのアクセス | jl_stackovf_exceptionをスロー |
| - | InterruptException | SIGINTを受信 | jl_interrupt_exceptionをスロー |
| - | DivideError | SIGFPE受信 | jl_diverror_exceptionをスロー |
| - | ReadOnlyMemoryError | 読取専用メモリへの書き込みSIGSEGV | jl_readonlymemory_exceptionをスロー |
| - | 致命的エラー | SIGILL, SIGABRT等 | sigdie_handlerでバックトレース出力後プロセス終了 |

### リトライ仕様

シグナルハンドラ自体のリトライはない。プロファイリング用タイマーは`timer_settime`で自動的に再設定される。

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

該当なし

## パフォーマンス要件

- シグナルハンドラはシグナルセーフ（async-signal-safe）な関数のみを使用する必要がある
- 8MBのシグナルスタック（`sig_stack_size`）が各スレッドに割り当てられる

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

- sigreturn oriented programming (SROP) 対策として、ucontextの変更は最小限に抑えている
- `jl_fake_signal_return`トランポリンによりCFI（Call Frame Information）情報を正確に維持

## 備考

- macOS（Darwin）ではMach exceptionsを使用する別実装（`signals-mach.c`）が`#include`される
- AArch64ダーウィンではSIGTRAPハンドラがLLVMのbrk #1をSIGILLとして再定義する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | julia_threads.h | `src/julia_threads.h` | jl_ptls_tのsignal_request, sig_exception, signal_stackフィールド |
| 1-2 | signals-unix.c | `src/signals-unix.c` | bt_context_t, x86_trap_flags, aarch64_esr_layoutの定義 |

**読解のコツ**: プラットフォーム固有の`#ifdef`ブロックが多いため、対象アーキテクチャを決めて読むとよい。`_CPU_X86_64_`かつ`_OS_LINUX_`の組み合わせが最も一般的な参照パスである。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | signals-unix.c | `src/signals-unix.c` | jl_install_default_signal_handlers（1311行目）でハンドラ登録 |
| 2-2 | signals-unix.c | `src/signals-unix.c` | restore_signals（1239行目）でシグナルリスナースレッド起動 |

**主要処理フロー**:
1. **1311-1387行目**: 各シグナルのsigactionによるハンドラ登録
2. **1239-1261行目**: シグナルマスク設定とsignal_listenerスレッド起動
3. **846-860行目**: segv_handlerのsigaction登録

#### Step 3: SIGSEGV処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | signals-unix.c | `src/signals-unix.c` | segv_handler（514行目） |

**主要処理フロー**:
- **517-521行目**: safe_restoreチェック（プロファイル復帰用）
- **527行目**: セーフポイントアドレスかつ読み取りフォルトの場合GC待機
- **549-551行目**: スタック上のアドレスならStackOverflowError
- **562-563行目**: 書き込みフォルトならReadOnlyMemoryError

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | signals-unix.c | `src/signals-unix.c` | usr2_handler（708行目） |
| 4-2 | signals-unix.c | `src/signals-unix.c` | jl_thread_suspend_and_get_state（576行目） |

**主要処理フロー**:
- **708-766行目**: signal_requestの値に応じた処理分岐（1: 状態取得, 2: SIGINT配信, 3: 終了, 4: no-op）
- **576-645行目**: eventfdを使ったスレッド一時停止プロトコル

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

```
jl_install_default_signal_handlers (signals-unix.c:1311)
    +-- sigaction(SIGFPE, fpe_handler)
    +-- sigaction(SIGSEGV, segv_handler)
    +-- sigaction(SIGUSR2, usr2_handler)
    +-- sigaction(SIGILL/SIGABRT, sigdie_handler)
    +-- allocate_segv_handler()

signal_listener (signals-unix.c:1043)
    +-- sigwait/kevent
    +-- jl_try_deliver_sigint (signals-unix.c:658)
    |       +-- jl_safepoint_enable_sigint()
    |       +-- pthread_kill(SIGUSR2)
    +-- do_profile (signals-unix.c:983)
    |       +-- jl_thread_suspend()
    |       +-- rec_backtrace_ctx()
    |       +-- jl_thread_resume()
    +-- jl_exit_thread0 (signals-unix.c:682)

segv_handler (signals-unix.c:514)
    +-- jl_addr_is_safepoint()
    +-- jl_set_gc_and_wait()
    +-- jl_throw_in_ctx()
    +-- sigdie_handler()
```

### データフロー図

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

OSシグナル ─────────────> シグナルハンドラ ──────────────> Julia例外
  SIGSEGV                   segv_handler                   StackOverflowError
  SIGFPE                    fpe_handler                    DivideError
  SIGINT                    signal_listener                InterruptException
                            usr2_handler
                                |
CPUコンテキスト ──────────> jl_to_bt_context ────────────> バックトレース
                                |
signal_request ─────────> usr2_handler ──────────────> スレッド状態取得/終了
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| signals-unix.c | `src/signals-unix.c` | ソース | Unix系OSのシグナル処理実装 |
| signals-mach.c | `src/signals-mach.c` | ソース | macOS Mach例外処理実装 |
| signals-win.c | `src/signals-win.c` | ソース | Windows例外処理実装 |
| signal-handling.c | `src/signal-handling.c` | ソース | プラットフォーム共通シグナル処理（signals-unix.cをinclude） |
| safepoint.c | `src/safepoint.c` | ソース | セーフポイント機構（シグナルと連携） |
| stackwalk.c | `src/stackwalk.c` | ソース | バックトレース取得 |
| julia_internal.h | `src/julia_internal.h` | ヘッダ | 内部関数宣言 |
