# 機能設計書 94-シグナル監視開始

## 概要

本ドキュメントは、libuvにおけるシグナル監視開始機能（uv_signal_start() / uv_signal_start_oneshot()）の設計を記述する。指定したシグナルの受信を監視し、コールバック関数を登録する。

### 本機能の処理概要

**業務上の目的・背景**：非同期イベント駆動アプリケーションにおいて、OSシグナルをイベントループのコールバックとして処理する必要がある。uv_signal_start()はシグナルハンドラをOSに登録し、シグナル受信時にイベントループ上でコールバックを実行する仕組みを構築する。ワンショットモード（uv_signal_start_oneshot()）では、シグナル受信後にハンドラが自動的に解除される。

**機能の利用シーン**：SIGINT/SIGTERMでのグレースフルシャットダウン、SIGHUP受信時の設定再読み込み、SIGUSR1/SIGUSR2によるカスタム処理のトリガー、ワンショット型のシグナルハンドリングで利用される。

**主要な処理内容**：
1. シグナル番号の妥当性検証（0でないこと）
2. 同一シグナル番号での再登録時はコールバックのみ更新（ショートサーキット）
3. 既存のシグナル監視が異なるシグナルに設定されている場合は先に停止
4. グローバルシグナルロックを取得し、シグナルハンドラをOSに登録
5. RBツリーにハンドルを挿入し、ハンドルをアクティブ状態にする

**関連システム・外部連携**：POSIX sigaction()システムコール、グローバルシグナルRBツリー、シグナルロック機構と連携する。

**権限による制御**：特になし。一般的なシグナル（SIGUSR1等）の監視には特別な権限は不要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | シグナル | 主機能 | uv_signal_start()/uv_signal_start_oneshot()によるシグナル監視開始のAPI定義 |

## 機能種別

イベント監視制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| handle | uv_signal_t* | Yes | 初期化済みのシグナルハンドル | NULLでないこと、クローズ中でないこと |
| signal_cb | uv_signal_cb | Yes | シグナル受信時のコールバック関数 | NULLでないこと |
| signum | int | Yes | 監視するシグナル番号 | 0でないこと、有効なシグナル番号 |

### 入力データソース

呼び出し元コードから直接パラメータとして渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時0、失敗時負のlibuvエラーコード |

### 出力先

呼び出し元への戻り値。OSへのシグナルハンドラ登録。グローバルRBツリーへのハンドル挿入。

## 処理フロー

### 処理シーケンス

```
1. クローズ中でないことをassertで検証
2. signum == 0 の場合は UV_EINVAL を返す
3. 同一シグナル番号で既に監視中の場合
   └─ コールバックのみ更新して 0 を返す（ショートサーキット）
4. 異なるシグナルで監視中の場合
   └─ uv__signal_stop() で既存の監視を停止
5. uv__signal_block_and_lock() で全シグナルブロック+グローバルロック取得
6. 同じsignumの最初のハンドルを検索
   ├─ ハンドルなし: sigaction()でOSシグナルハンドラを登録
   └─ ワンショットのみ存在 + 通常ハンドル追加: ハンドラを再登録
7. RBツリーにハンドルを挿入
8. uv__signal_unlock_and_unblock() でロック解放+シグナル復帰
9. コールバックを設定し、ハンドルをアクティブ化
10. 0 を返す
```

### フローチャート

```mermaid
flowchart TD
    A[uv_signal_start 呼び出し] --> B{signum == 0?}
    B -->|Yes| C[UV_EINVAL 返却]
    B -->|No| D{同一signum で監視中?}
    D -->|Yes| E[コールバック更新のみ]
    D -->|No| F{別signum で監視中?}
    F -->|Yes| G[uv__signal_stop]
    F -->|No| H[グローバルロック取得]
    G --> H
    H --> I{初回ハンドラ?}
    I -->|Yes| J[sigaction でOSハンドラ登録]
    I -->|No| K[RBツリーに挿入]
    J --> K
    K --> L[ロック解放]
    L --> M[コールバック設定 + アクティブ化]
    M --> N[0 返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-94-1 | シグナル0拒否 | signum=0は無効として即座にUV_EINVALを返す | signum==0の場合 |
| BR-94-2 | ショートサーキット | 同一signumで監視中の場合はコールバック更新のみ | 既に同じsignumで監視中の場合 |
| BR-94-3 | ワンショット | uv_signal_start_oneshot()ではUV_SIGNAL_ONE_SHOTフラグが設定される | ワンショットモード使用時 |
| BR-94-4 | ハンドラ再登録 | ワンショットハンドラのみ存在する状態で通常ハンドラが追加される場合、OSハンドラを再登録 | 該当条件発生時 |
| BR-94-5 | SA_RESTART | シグナルハンドラはSA_RESTARTフラグ付きで登録される | 常時 |
| BR-94-6 | SA_RESETHAND | ワンショットモードではSA_RESETHANDフラグも設定される | oneshot==1の場合 |

### 計算ロジック

特になし。

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

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | 無効パラメータ | signum=0が指定された | 有効なシグナル番号を指定する |
| UV_EINVAL | 無効シグナル | sigaction()が失敗（無効なシグナル番号） | 有効なシグナル番号を使用する |

### リトライ仕様

リトライは不要。

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

該当なし。

## パフォーマンス要件

同期的な処理であり、通常はマイクロ秒オーダーで完了する。グローバルロック取得が含まれるため、高頻度呼び出しは避けるべき。

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

- シグナルハンドラは全シグナルをブロックした状態で登録される（sa_maskがfillset）
- グローバルシグナルロックによりスレッドセーフ性が保証される

## 備考

- uv_signal_start()とuv_signal_start_oneshot()は内部的にuv__signal_start()の共通実装を呼び出し、oneshotパラメータの違いのみ
- RBツリーのソート順はsignum、UV_SIGNAL_ONE_SHOTフラグ、ループポインタ、ハンドルアドレスの順

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **1720-1725行目**: uv_signal_s構造体 |
| 1-2 | signal.c | `src/unix/signal.c` | **40行目**: RB_HEAD宣言。グローバルRBツリーの定義 |
| 1-3 | signal.c | `src/unix/signal.c` | **503-530行目**: uv__signal_compare()。RBツリーのソート基準 |

**読解のコツ**: RBツリーはFreeBSDのsys/tree.hベースのマクロで実装されている。RB_INSERTやRB_NFINDなどのマクロ呼び出しが多い。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | signal.c | `src/unix/signal.c` | **362-364行目**: uv_signal_start()。uv__signal_start()への委譲（oneshot=0） |
| 2-2 | signal.c | `src/unix/signal.c` | **367-371行目**: uv_signal_start_oneshot()。uv__signal_start()への委譲（oneshot=1） |

#### Step 3: 内部実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | signal.c | `src/unix/signal.c` | **374-435行目**: uv__signal_start()の実装全体 |

**主要処理フロー**:
- **382行目**: `assert(!uv__is_closing(handle))` - クローズ中でないことを検証
- **388-389行目**: signum==0のチェック
- **396-399行目**: 同一signumの場合のショートサーキット
- **402-404行目**: 既存監視の停止
- **406行目**: `uv__signal_block_and_lock()` - シグナルブロック+ロック取得
- **412-421行目**: 初回ハンドラの登録（sigaction経由）
- **427行目**: `RB_INSERT()` - RBツリーへの挿入
- **431-432行目**: コールバック設定とハンドルアクティブ化

#### Step 4: OSシグナルハンドラの登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | signal.c | `src/unix/signal.c` | **223-241行目**: uv__signal_register_handler()。sigaction()によるハンドラ登録 |
| 4-2 | signal.c | `src/unix/signal.c` | **182-220行目**: uv__signal_handler()。実際のシグナルハンドラ。パイプへの書き込み |

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

```
uv_signal_start(handle, signal_cb, signum)
    |
    +-- uv__signal_start(handle, signal_cb, signum, oneshot=0)
            |
            +-- uv__signal_stop(handle)  [既存の異なるsignum監視がある場合]
            |
            +-- uv__signal_block_and_lock(&saved_sigmask)
            |
            +-- uv__signal_first_handle(signum)  [RBツリー検索]
            |
            +-- uv__signal_register_handler(signum, oneshot)  [初回のみ]
            |       |
            |       +-- sigaction(signum, &sa, NULL)
            |
            +-- RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle)
            |
            +-- uv__signal_unlock_and_unblock(&saved_sigmask)
            |
            +-- uv__handle_start(handle)
```

### データフロー図

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

uv_signal_t*        ---> 状態検証           --->
uv_signal_cb        ---> ハンドラ登録       ---> OSシグナルテーブル
int signum          ---> RBツリー挿入       ---> グローバルRBツリー
                         アクティブ化       ---> int (0 or error)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | API宣言とuv_signal_t構造体定義 |
| signal.c | `src/unix/signal.c` | ソース | Unix系シグナル処理の全実装 |
| signal.c | `src/win/signal.c` | ソース | Windowsシグナル処理実装 |
