# 通知設計書 12-exit

## 概要

本ドキュメントは、LSP標準通知 `exit` の設計仕様を定義する。この通知はクライアントからサーバーに送信され、サーバープロセスの終了を要求する。

### 本通知の処理概要

`exit` 通知は、LSPプロトコルにおけるサーバーのライフサイクル終了を制御する通知である。通常は `shutdown` リクエストの後に送信され、サーバープロセスを安全に終了させる。serve-dでは `serverbase.d` の `processNotify` 関数内で直接ハンドリングされる。

**業務上の目的・背景**：LSPサーバーのライフサイクル管理において、サーバープロセスを正しく終了させることは重要である。`exit` 通知は `shutdown` リクエスト後にサーバーを実際に停止させる最終ステップとして機能する。不正な終了を防ぎ、リソースの適切な解放を保証する。

**通知の送信タイミング**：クライアントが `shutdown` リクエストを送信しレスポンスを受け取った後に送信される。LSP仕様ではこの順序が規定されている。ただし、serve-dの実装では `shutdownRequested` フラグが立っている場合、`exit` 通知を受信しなくても次の任意の通知でRPCが停止する。

**通知の受信者**：serve-dサーバープロセスが受信者である。`serverbase.d` の `processNotify` メソッド内で直接処理される（専用のハンドラ関数は存在しない）。

**通知内容の概要**：パラメータは空（なし）である。メソッド名 `exit` のみで十分であり、追加データは不要である。

**期待されるアクション**：サーバーはRPCループを停止し、`shutdownRequested` が未設定の場合は `shutdown` メソッドを呼び出した上で、プロセスを終了する。

## 通知種別

LSPプロトコル通知（JSON-RPC notification）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（JSON-RPC notification） |
| 優先度 | 高（サーバー終了制御） |
| リトライ | 無し（通知のため応答なし） |

### 送信先決定ロジック

クライアントからサーバーへの一方向通知。LSP接続で確立されたサーバープロセスに固定。

## 通知テンプレート

### JSON-RPC通知メッセージ

| 項目 | 内容 |
|-----|------|
| メソッド名 | `exit` |
| パラメータ型 | なし |
| レスポンス | なし（通知） |

### メッセージ構造

```json
{
  "jsonrpc": "2.0",
  "method": "exit",
  "params": {}
}
```

### 添付ファイル

該当なし。

## テンプレート変数

該当なし（パラメータなし）。

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プロトコル操作 | shutdownレスポンス受信後 | shutdown完了 | クライアントがshutdownリクエストを送信し、レスポンスを受け取った後に送信 |
| 画面操作 | エディタ終了時 | なし | ユーザーがエディタを閉じた場合にクライアントが自動送信 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | exit通知に抑止条件は設定されていない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[クライアント: shutdown完了] --> B[exit通知送信]
    B --> C[サーバー: processNotify受信]
    C --> D{method == exit OR shutdownRequested?}
    D -->|Yes| E[rpc.stop 呼び出し]
    E --> F{shutdownRequested?}
    F -->|No| G[shutdownRequested = true]
    G --> H[ExtensionModule.shutdown 呼び出し]
    F -->|Yes| I[RPCループ終了]
    H --> I
    I --> J[プロセス終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

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

### 更新テーブル一覧

該当なし（データベースを使用しない）。`shutdownRequested` フラグと `backend` のシャットダウン処理のみ。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| shutdown前のexit | shutdownリクエストなしでexit受信 | shutdownRequested=trueに設定しshutdown処理を実行してから終了 |
| shutdown後のタイムアウト | exit通知が来ない | extension.dのshutdown関数内で1秒後にError例外をスロー |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | N/A（1回限り） |
| 1日あたり上限 | N/A（1回限り） |

### 配信時間帯

制限なし。サーバーライフサイクルの最終フェーズで1回のみ送信される。

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

- 個人情報は含まれない
- サーバープロセス終了を引き起こす重要な通知であるが、LSP接続はクライアント-サーバー間の1:1通信のため、第三者による不正送信のリスクは低い

## 備考

- LSP仕様では `shutdown` リクエスト後に `exit` 通知を送信する流れが正式なプロトコルである
- serve-dの実装では、`shutdownRequested` が `true` の場合、`exit` 通知に限らず**任意の通知**でRPCループが停止する（serverbase.d L354）
- vscode-languageserverがshutdown後にexitを呼ばないケースがあるため、この実装が採用されている（serverbase.d L352コメント参照）
- `extension.d` の `shutdown` 関数（L1078-1088）では、バックエンドのシャットダウン後に1秒のタイムアウトを設定している

---

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

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

### 推奨読解順序

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

exit通知にはパラメータがないため、データ構造の事前確認は不要。関連する状態変数を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | extension.d | `source/served/extension.d` | `shutdownRequested` グローバル変数（L57）の定義 |

**読解のコツ**: `__gshared` はD言語でスレッド間で共有されるグローバル変数を宣言するアトリビュートである。

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

processNotify内のexit処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | serverbase.d | `serverbase/source/served/serverbase.d` | `processNotify` 関数（L348-387）内のexit処理ブロック（L352-364） |

**主要処理フロー**:
1. **L354**: `method == "exit"` または `shutdownRequested` を確認
2. **L356**: `rpc.stop()` を呼び出してRPCループ停止
3. **L357-363**: `shutdownRequested` が未設定なら設定し、`ExtensionModule.shutdown()` を呼び出す

#### Step 3: shutdown関数を理解する

exit通知の前段として呼ばれるshutdown処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | extension.d | `source/served/extension.d` | `shutdown` 関数（L1078-1088）の実装 |

**主要処理フロー**:
- **L1078**: `@protocolMethod("shutdown")` でリクエストハンドラとして登録
- **L1081-1083**: `backend` が存在すれば `backend.shutdown()` を呼び出す
- **L1084-1086**: 1秒後にタイムアウトエラーを発生させるタイマーを設定

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

```
[JSON-RPC受信] processNotify (serverbase.d:348)
    |
    +-- [method == "exit" || shutdownRequested] (L354)
            |
            +-- rpc.stop() (L356)
            |
            +-- [!shutdownRequested の場合]
                    |
                    +-- shutdownRequested = true (L359)
                    +-- ExtensionModule.shutdown() (L361)
                            |
                            +-- backend.shutdown() (extension.d:1083)
```

### データフロー図

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

exit通知           processNotify()                    RPCループ停止
(パラメータなし) --> (serverbase.d:348-364) -------> rpc.stop()
                   shutdownRequested判定              |
                   |                                  +-> backend.shutdown()
                   +-> Extension.shutdown()                (リソース解放)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| serverbase.d | `serverbase/source/served/serverbase.d` | ソース | processNotifyでのexit通知ハンドリング |
| extension.d | `source/served/extension.d` | ソース | shutdownRequested変数定義とshutdown関数実装 |
| types.d | `source/served/types.d` | ソース | LanguageServerRouter mixinの適用 |
