# 機能設計書 100-ジョブスケジューリング

## 概要

本ドキュメントはNushellのジョブスケジューリング機能について記述する。バックグラウンドジョブの生成・管理・終了を行う機能の設計を定義する。

### 本機能の処理概要

ジョブスケジューリング機能は、Nushellスクリプトをバックグラウンドスレッドで実行し、その状態を管理する。ジョブの生成（spawn）、一覧表示（list）、終了（kill）などの操作を提供する。

**業務上の目的・背景**：シェルにおいて時間のかかる処理をバックグラウンドで実行し、フォアグラウンドで別の作業を行うことは一般的なユースケースである。Nushellのジョブ機能は、この並行処理パターンを構造化された方法で提供する。

**機能の利用シーン**：
- 時間のかかる処理のバックグラウンド実行
- 複数タスクの並行処理
- 長時間実行タスクの管理と監視
- ジョブ間のメッセージング

**主要な処理内容**：
1. クロージャのバックグラウンドスレッド実行（job spawn）
2. ジョブ一覧の取得（job list）
3. ジョブの強制終了（job kill）
4. ジョブ間メッセージング（job send/recv）

**関連システム・外部連携**：OSのプロセス管理、スレッド管理

**権限による制御**：特になし。プロセス終了権限はOSの権限に従う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はCLIコマンドとして提供され、特定の画面とは関連しない |

## 機能種別

ジョブ管理 / 並行処理 / バックグラウンド実行

## 入力仕様

### 入力パラメータ

#### job spawn

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| closure | Closure | Yes | バックグラウンドで実行するクロージャ | - |
| --tag, -t | String | No | ジョブの説明タグ | - |

#### job list

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| (なし) | - | - | - | - |

#### job kill

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Int | Yes | 終了するジョブのID | 有効なジョブID |

### 入力データソース

- コマンドライン引数

## 出力仕様

### 出力データ

#### job spawn

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | Int | 生成されたジョブのID |

#### job list

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Int | ジョブID |
| type | String | ジョブ種別（thread/frozen） |
| pids | List<Int> | 関連するプロセスID |
| tag | String | ジョブのタグ（オプション） |

#### job kill

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | Nothing | 出力なし |

### 出力先

- パイプライン出力

## 処理フロー

### 処理シーケンス

```
job spawn:
1. クロージャの取得
2. エンジン状態のクローン
   ├─ is_interactive = false
   ├─ 独立したSignals設定
   └─ 独立したプロセスグループ状態
3. mpscチャネルの作成
4. ThreadJobの生成とジョブテーブルへの追加
5. スレッド生成
   ├─ stdout/stderrをNullにリダイレクト
   └─ クロージャ実行
6. ジョブIDを返却

job list:
1. ジョブテーブルのロック取得
2. 各ジョブの情報を収集
3. テーブルとして返却

job kill:
1. ジョブIDの検証
2. ジョブの検索
3. kill_and_remove
   ├─ signals.trigger()
   ├─ 各PIDをkill
   └─ ジョブテーブルから削除
```

### フローチャート

```mermaid
flowchart TD
    A[job spawn] --> B[クロージャ取得]
    B --> C[エンジン状態クローン]
    C --> D[独立したSignals設定]
    D --> E[mpscチャネル作成]
    E --> F[ThreadJob生成]
    F --> G[ジョブテーブル登録]
    G --> H[スレッド生成]
    H --> I[クロージャ実行]
    I --> J[完了時にジョブ削除]
    G --> K[ジョブID返却]

    L[job kill] --> M[ジョブID検証]
    M --> N{ジョブ存在?}
    N -->|No| O[NotFoundエラー]
    N -->|Yes| P[signals.trigger]
    P --> Q[各PIDをkill]
    Q --> R[ジョブテーブル削除]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-100-01 | 独立したCtrl-C | バックグラウンドジョブはフォアグラウンドと独立したシグナル | job spawn |
| BR-100-02 | 出力リダイレクト | stdout/stderrはNullにリダイレクト | job spawn |
| BR-100-03 | 非対話モード | バックグラウンドジョブはis_interactive=false | job spawn |
| BR-100-04 | 自動削除 | ジョブ完了時にジョブテーブルから自動削除 | ジョブ完了時 |
| BR-100-05 | ジョブID連番 | ジョブIDは1から始まる連番 | job spawn |

### 計算ロジック

**ジョブID管理**:
- Jobs.next_job_idから割り当て
- 使用後にインクリメント
- 削除されたIDは再利用されない

**プロセス管理**:
- ThreadJob.pidsでアクティブなPIDを追跡
- kill時はsignals.trigger()で割り込み
- 各PIDに対してkill_by_pid()を実行

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| JobError::NotFound | ShellError | 指定IDのジョブが存在しない | 有効なジョブIDを指定 |
| IoError | ShellError | スレッド生成失敗 | システムリソースを確認 |
| IoError | ShellError | プロセスkill失敗 | 権限を確認 |

### リトライ仕様

特になし。

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

- ジョブテーブルはMutexで保護
- kill_and_remove: 常にジョブを削除（kill失敗時も）

## パフォーマンス要件

- ジョブ生成は即座に完了
- ジョブテーブル操作はロック競合を最小化

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

- ジョブの出力はNullにリダイレクト（情報漏洩防止）
- 独立したシグナルでフォアグラウンドへの影響を分離
- プロセスkillはOSの権限に従う

## 備考

- 実験的機能としてCategory::Experimentalに分類
- FrozenJobは停止中のプロセス（Ctrl-Z等）を表す
- メールボックスはジョブ間通信に使用

---

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

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

### 推奨読解順序

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

ジョブ管理のデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | jobs.rs | `crates/nu-protocol/src/engine/jobs.rs` | Jobs構造体（18-26行目） |
| 1-2 | jobs.rs | `crates/nu-protocol/src/engine/jobs.rs` | Job列挙型（130-133行目） |
| 1-3 | jobs.rs | `crates/nu-protocol/src/engine/jobs.rs` | ThreadJob構造体（143-149行目） |

**読解のコツ**: Jobsはjobsフィールド（HashMap<JobId, Job>）でジョブを管理。JobはThreadとFrozenの2種類。ThreadJobは独立したSignalsとpids（HashSet）を持つ。

#### Step 2: job spawnを理解する

ジョブ生成の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | job_spawn.rs | `crates/nu-command/src/experimental/job_spawn.rs` | run メソッド（50-131行目） |

**主要処理フロー（job_spawn.rs）**:
1. **59行目**: クロージャの取得
2. **64-74行目**: エンジン状態のクローンと設定
3. **79行目**: mpscチャネル作成
4. **82-93行目**: ThreadJobの生成とジョブテーブル登録
5. **95-117行目**: スレッド生成とクロージャ実行

#### Step 3: job listを理解する

ジョブ一覧表示の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | job_list.rs | `crates/nu-command/src/experimental/job_list.rs` | run メソッド（26-70行目） |

**主要処理フロー（job_list.rs）**:
1. **35行目**: ジョブテーブルのロック取得
2. **37-67行目**: 各ジョブの情報をレコードに変換
3. **69行目**: リストとして返却

#### Step 4: job killを理解する

ジョブ終了の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | job_kill.rs | `crates/nu-command/src/experimental/job_kill.rs` | run メソッド（28-55行目） |
| 4-2 | jobs.rs | `crates/nu-protocol/src/engine/jobs.rs` | kill_and_remove（95-105行目） |
| 4-3 | jobs.rs | `crates/nu-protocol/src/engine/jobs.rs` | ThreadJob::kill（183-198行目） |

**主要処理フロー（job_kill.rs）**:
1. **37-38行目**: ジョブIDの取得
2. **42-44行目**: ジョブの存在確認
3. **46-52行目**: kill_and_remove呼び出し

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

```
job spawn
    │
    ├─ closure取得
    │
    ├─ engine_state.clone()
    │      ├─ is_interactive = false
    │      ├─ Signals::new(AtomicBool::new(false))
    │      └─ pipeline_externals_state = Arc::new(...)
    │
    ├─ mpsc::channel()
    │
    ├─ Jobs::add_job(Job::Thread(ThreadJob))
    │      └─ ジョブID割り当て
    │
    └─ thread::Builder::new().spawn()
           │
           ├─ stack.reset_pipes()
           ├─ push_redirection(OutDest::Null)
           │
           ├─ ClosureEvalOnce::run_with_input()
           │      └─ data.drain()
           │
           └─ Jobs::remove_job(id)

job list
    │
    └─ Jobs::iter()
           └─ 各ジョブの情報収集
                  ├─ id
                  ├─ type (thread/frozen)
                  ├─ pids (collect_pids())
                  └─ tag

job kill
    │
    ├─ Jobs::lookup(id)
    │
    └─ Jobs::kill_and_remove(id)
           │
           ├─ Job::kill()
           │      ├─ signals.trigger()
           │      ├─ pids.lock()
           │      └─ kill_by_pid(pid)
           │
           └─ Jobs::remove_job(id)
```

### データフロー図

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

job spawn closure  ───────▶  engine_state.clone()  ────────▶  ジョブID
                             │
                             ├─ ThreadJob::new()
                             │      └─ Signals, pids, sender
                             │
                             ├─ Jobs::add_job()
                             │
                             └─ thread::spawn()
                                    │
                                    ├─ ClosureEvalOnce
                                    │
                                    └─ Jobs::remove_job()

job list         ─────────▶  Jobs::iter()         ────────▶  テーブル
                             │                               [id, type, pids, tag]
                             └─ 各ジョブの情報収集

job kill id      ─────────▶  Jobs::kill_and_remove()  ────▶  Nothing
                             │
                             ├─ signals.trigger()
                             ├─ kill_by_pid()
                             └─ remove_job()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| jobs.rs | `crates/nu-protocol/src/engine/jobs.rs` | ソース | Jobs, Job, ThreadJob, Mailbox等のデータ構造 |
| job_spawn.rs | `crates/nu-command/src/experimental/job_spawn.rs` | ソース | job spawnコマンド |
| job_list.rs | `crates/nu-command/src/experimental/job_list.rs` | ソース | job listコマンド |
| job_kill.rs | `crates/nu-command/src/experimental/job_kill.rs` | ソース | job killコマンド |
| job_send.rs | `crates/nu-command/src/experimental/job_send.rs` | ソース | job sendコマンド |
| job_recv.rs | `crates/nu-command/src/experimental/job_recv.rs` | ソース | job recvコマンド |
| job_tag.rs | `crates/nu-command/src/experimental/job_tag.rs` | ソース | job tagコマンド |
| job_id.rs | `crates/nu-command/src/experimental/job_id.rs` | ソース | job idコマンド |
| job_unfreeze.rs | `crates/nu-command/src/experimental/job_unfreeze.rs` | ソース | job unfreezeコマンド |
