# 機能設計書 36-_omz_async_request

## 概要

本ドキュメントは、Oh My Zshの`_omz_async_request`関数の機能設計について記載する。この関数は、登録されたすべての非同期ハンドラをサブシェルで実行し、その結果をファイルディスクリプタ経由で受け取れるようにする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：プロンプト表示時にGit情報などの動的な情報を取得する場合、同期的に実行するとシェルの応答性が低下する。この関数は、重い処理をサブシェルでバックグラウンド実行し、プロンプト表示をブロックせずに情報を取得する仕組みを提供する。precmdフックから呼び出され、毎回のプロンプト表示前に非同期処理を開始する。

**機能の利用シーン**：プロンプト表示の直前（precmdフック）で自動的に呼び出され、`_omz_register_handler`で登録されたすべてのハンドラを非同期実行する。

**主要な処理内容**：
1. 各登録済みハンドラについてループ処理
2. 保留中のリクエストがあればキャンセル（プロセス終了、FDクローズ）
3. サブシェルでハンドラを実行し、パイプを作成
4. ファイルディスクリプタを`zle -F`でコールバック登録
5. サブシェルのPIDを保存

**関連システム・外部連携**：ZSHのzleモジュール（`zle -F`）を使用してファイルディスクリプタの監視を登録する。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ターミナルプロンプト | 結果表示画面 | 非同期処理の結果がプロンプトに反映される |

## 機能種別

非同期処理制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| なし | - | - | 引数は受け取らない（グローバル変数を参照） | - |

### 入力データソース

- **グローバル配列**: `_omz_async_functions` - 登録済みハンドラ関数名の配列
- **グローバル連想配列**: `_OMZ_ASYNC_FDS` - ハンドラごとのファイルディスクリプタ
- **グローバル連想配列**: `_OMZ_ASYNC_PIDS` - ハンドラごとのプロセスID

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| _OMZ_ASYNC_FDS[$handler] | int | 各ハンドラに対応するファイルディスクリプタ |
| _OMZ_ASYNC_PIDS[$handler] | int | 各ハンドラに対応するサブシェルのPID |

### 出力先

- グローバル連想配列 `_OMZ_ASYNC_FDS`
- グローバル連想配列 `_OMZ_ASYNC_PIDS`
- `zle -F` でコールバック登録

## 処理フロー

### 処理シーケンス

```
1. シェルオプションの一時設定
   └─ noksharrays、unsetを有効化
2. 直前のコマンド終了コードを保存
3. グローバル連想配列の宣言
4. 各ハンドラについてループ
   4.1 ハンドラが関数として存在するか確認
   4.2 保留中リクエストがあれば:
       └─ FDをクローズし、zle -Fハンドラを削除
       └─ プロセスをkill（MONITORオプションに応じてプロセスグループまたは単一プロセス）
   4.3 FD/PID変数を-1に初期化
   4.4 サブシェルを起動してパイプを作成
       └─ サブシェル内: PIDを出力、終了コード設定、ハンドラ実行
   4.5 FDを保存
   4.6 zsh 5.8未満の場合のバグ回避
   4.7 サブシェルからPIDを読み取って保存
   4.8 zle -FでFDにコールバックを登録
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[シェルオプション設定]
    B --> C[終了コード保存]
    C --> D[グローバル配列宣言]
    D --> E{ハンドラループ}
    E --> F{関数が存在?}
    F -->|No| E
    F -->|Yes| G{保留中リクエスト?}
    G -->|Yes| H[FDクローズ/プロセスkill]
    G -->|No| I[FD/PID初期化]
    H --> I
    I --> J[サブシェル起動/パイプ作成]
    J --> K[FD保存]
    K --> L[PID読み取り/保存]
    L --> M[zle -Fでコールバック登録]
    M --> E
    E -->|完了| N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-36-1 | リクエストキャンセル | 新しいリクエスト発行時に保留中の古いリクエストをキャンセルする | 保留中リクエスト存在時 |
| BR-36-2 | プロセスグループkill | MONITORオプション有効時はプロセスグループ全体をkillする | MONITOR有効時 |
| BR-36-3 | 単一プロセスkill | MONITORオプション無効時は単一プロセスのみkillする | MONITOR無効時 |
| BR-36-4 | 終了コード継承 | ハンドラに直前のコマンド終了コードを渡す | 常時 |

### 計算ロジック

特になし

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

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

該当なし（データベースを使用しない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 暗黙のエラー | ハンドラ関数が未定義 | スキップして次のハンドラへ |

### リトライ仕様

リトライは実装されていない（次回precmdで再実行される）

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

該当なし

## パフォーマンス要件

- precmdフック内で実行されるため、サブシェル起動は最小限のオーバーヘッドで完了する必要がある
- 実際のハンドラ処理はサブシェル内で非同期に実行される

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

- `GIT_OPTIONAL_LOCKS=0` は継承されない（サブシェル内でハンドラが再設定する必要がある）
- プロセスkillはTERMシグナルを使用（強制終了ではない）

## 備考

- zsh 5.8未満には`command true`によるバグ回避が必要
- 参照元: zsh-autosuggestions、git-prompt.zsh

---

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

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

### 推奨読解順序

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

非同期プロンプトシステムで使用されるグローバル変数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | async_prompt.zsh | `lib/async_prompt.zsh` | グローバル変数の役割を確認 |

**読解のコツ**:
- `_omz_async_functions`: 登録されたハンドラ名の配列
- `_OMZ_ASYNC_FDS`: ハンドラ名 -> FDの連想配列
- `_OMZ_ASYNC_PIDS`: ハンドラ名 -> PIDの連想配列
- `_OMZ_ASYNC_OUTPUT`: ハンドラ名 -> 出力の連想配列

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_async_request`関数の定義（46-107行目） |

**主要処理フロー**:
1. **47行目**: `setopt localoptions noksharrays unset` - シェルオプション設定
2. **48行目**: `local -i ret=$?` - 直前の終了コードを保存
3. **49行目**: `typeset -gA _OMZ_ASYNC_FDS _OMZ_ASYNC_PIDS _OMZ_ASYNC_OUTPUT` - 連想配列宣言
4. **53-54行目**: ハンドラループ開始、関数存在チェック
5. **56-57行目**: 既存のFD/PIDを取得
6. **60-77行目**: 保留中リクエストのキャンセル処理
7. **80-81行目**: FD/PID変数の初期化
8. **84-91行目**: サブシェル起動、パイプ作成
9. **94行目**: FDを保存
10. **99行目**: zsh 5.8未満のバグ回避
11. **102行目**: PIDを読み取り
12. **105行目**: `zle -F "$fd" _omz_async_callback` - コールバック登録

#### Step 3: コールバック関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_async_callback`関数（110-142行目） |

#### Step 4: 登録関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | async_prompt.zsh | `lib/async_prompt.zsh` | `_omz_register_handler`関数（28-43行目） |

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

```
precmd hook
    │
    └─ _omz_async_request()
           │
           ├─ 保留中リクエストのキャンセル
           │      ├─ exec {fd}<&- (FDクローズ)
           │      ├─ zle -F $fd (ハンドラ削除)
           │      └─ kill -TERM -$pid または kill -TERM $pid
           │
           ├─ サブシェル起動
           │      └─ exec {fd}< <(
           │            builtin echo ${sysparams[pid]}
           │            () { return $ret }
           │            $handler
           │         )
           │
           └─ zle -F "$fd" _omz_async_callback
```

### データフロー図

```
[precmd]            [サブシェル]              [コールバック]

precmd実行 ───▶ _omz_async_request
                      │
                      ├─ サブシェル起動 ─────▶ PID出力
                      │                       │
                      │                       ▼
                      │                    ハンドラ実行
                      │                       │
                      │                       ▼
                      │                    結果をパイプに出力
                      │
                      └─ zle -F登録 ────────────────▶ _omz_async_callback
                                                          │
                                                          ▼
                                                    _OMZ_ASYNC_OUTPUT更新
                                                          │
                                                          ▼
                                                    zle .reset-prompt
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| async_prompt.zsh | `lib/async_prompt.zsh` | ソース | _omz_async_request関数の定義元 |
| git.zsh | `lib/git.zsh` | ソース | 登録されるハンドラの定義元 |
| oh-my-zsh.sh | `oh-my-zsh.sh` | ソース | lib/async_prompt.zshを読み込むメインスクリプト |
