# 機能設計書 83-非同期処理

## 概要

本ドキュメントは、lua-language-serverにおける非同期処理機能について記載する。この機能は、Luaのコルーチンを活用した非同期処理フレームワークを提供し、LSPサーバーの応答性を維持しながら重い処理を実行可能にする。

### 本機能の処理概要

**業務上の目的・背景**：Language Serverは、コード補完、診断、参照検索など、処理時間が長くなる可能性のある機能を多数提供する。これらの処理を同期的に実行すると、エディタのUI応答性が低下する。本機能は、コルーチンベースの非同期処理フレームワークを提供することで、長時間処理を分割実行し、サーバーの応答性を維持する。また、処理のキャンセル機能やID管理による一括終了機能も提供する。

**機能の利用シーン**：LSPリクエスト処理、ファイル解析、診断実行、ワークスペーススキャンなど、時間のかかる処理すべてで利用される。特に、ユーザーが編集中にバックグラウンドで診断を実行する場合や、大規模プロジェクトのシンボル検索時に活用される。

**主要な処理内容**：
1. コルーチンベースのタスク作成と実行（await.call）
2. 非同期待機と再開（await.wait, await.sleep）
3. 処理の遅延実行とスケジューリング（await.delay）
4. タスクIDによる一括キャンセル（await.close, await.setID）
5. スロットリング付き遅延（ThrottledDelayer）
6. ステップ実行によるイベントループ統合（await.step）

**関連システム・外部連携**：タイマーモジュール（timer.lua）と連携してスリープ処理を実現する。プロトコルモジュール（proto.lua）から呼び出され、LSPリクエストの非同期処理を提供する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面を持たず、バックエンド非同期処理基盤として動作 |

## 機能種別

ユーティリティ / 非同期処理フレームワーク

## 入力仕様

### 入力パラメータ

#### await.call関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| callback | function | Yes | 実行する非同期関数 | 関数であること |
| ... | string | No | タスクに割り当てるID（複数可） | - |

#### await.sleep関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| time | number | Yes | スリープ時間（ミリ秒） | 正の数値 |

#### await.wait関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| callback | function | Yes | resume関数を受け取るコールバック | 関数であること |
| ... | any | No | コールバックに渡す追加引数 | - |

#### await.close関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 終了するタスクのID | 文字列であること |

#### await.setID関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | タスクに割り当てるID | 文字列であること |
| callback | function | No | クローズ時に呼び出されるコールバック | 関数であること |

### 入力データソース

- LSPハンドラーからの非同期処理リクエスト
- タイマーからのウェイクアップイベント

## 出力仕様

### 出力データ

#### await.call関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| success | boolean | 実行成功/失敗 |
| result | any | コールバックの戻り値またはエラーメッセージ |

#### await.wait関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ... | any | resume関数に渡された引数 |

#### await.step関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| hasMore | boolean | 未処理のタスクがあるかどうか |

### 出力先

- コルーチンの戻り値として呼び出し元に返却
- エラーハンドラー（setErrorHandleで設定）への通知

## 処理フロー

### 処理シーケンス

```
await.call処理:
1. コルーチンを作成
   └─ coroutine.create(callback)
2. タスク情報をcoMapに登録
   └─ closers, priority情報を保持
3. 指定されたIDを登録
   └─ idMapにコルーチンを関連付け
4. 親タスクのclosersを継承
   └─ 入れ子タスクの管理
5. コルーチンを開始
   └─ coroutine.resume(co)

await.sleep処理:
1. yield可能かチェック
   └─ coroutine.isyieldable()
2. タイマーを設定
   └─ timer.wait(time, callback)
3. コルーチンをサスペンド
   └─ coroutine.yield()
4. タイマー発火でresume
   └─ coroutine.resume(co)

await.delay処理:
1. yield可能かチェック
2. delayQueueにresume関数を追加
3. コルーチンをサスペンド
4. await.step()で順次resume

await.close処理:
1. IDに関連するコルーチンを取得
2. サスペンド中のコルーチンをクローズ
3. クローズコールバックを実行
```

### フローチャート

```mermaid
flowchart TD
    subgraph call["await.call処理"]
        C1[コルーチン作成] --> C2[coMap登録]
        C2 --> C3[ID登録]
        C3 --> C4[closers継承]
        C4 --> C5[coroutine.resume]
        C5 --> C6{成功?}
        C6 -->|Yes| C7[結果返却]
        C6 -->|No| C8[errorHandle呼び出し]
    end

    subgraph sleep["await.sleep処理"]
        S1{yield可能?} -->|No| S2[エラー通知]
        S1 -->|Yes| S3[timer.wait設定]
        S3 --> S4[coroutine.yield]
        S4 --> S5[タイマー発火]
        S5 --> S6[coroutine.resume]
    end

    subgraph delay["await.delay処理"]
        D1{_enable?} -->|No| D2[即リターン]
        D1 -->|Yes| D3{yield可能?}
        D3 -->|No| D4[即リターン]
        D3 -->|Yes| D5{priority?}
        D5 -->|Yes| D6[即リターン]
        D5 -->|No| D7[delayQueue追加]
        D7 --> D8[coroutine.yield]
    end

    subgraph step["await.step処理"]
        T1[needCloseを処理] --> T2{delayQueueに要素?}
        T2 -->|No| T3[キュークリア]
        T3 --> T4[false返却]
        T2 -->|Yes| T5[resume取り出し]
        T5 --> T6[resume実行]
        T6 --> T7{0.5秒超?}
        T7 -->|Yes| T8[警告ログ]
        T7 -->|No| T9[true返却]
        T8 --> T9
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-83-01 | yield可能性チェック | yield不可能なコンテキストではエラー処理またはスキップ | sleep, wait, delay呼び出し時 |
| BR-83-02 | ID管理 | 同一IDのタスクは一括でクローズ可能 | await.close呼び出し時 |
| BR-83-03 | closers継承 | 子タスクは親タスクのclosersを継承 | await.call呼び出し時 |
| BR-83-04 | priority処理 | priorityフラグがtrueのタスクはdelay処理をスキップ | await.delay呼び出し時 |
| BR-83-05 | 弱参照管理 | coMapとidMapは弱参照でGC可能 | タスク管理全般 |
| BR-83-06 | 処理時間警告 | ステップ処理が0.5秒を超えた場合、警告ログを出力 | await.step実行時 |

### 計算ロジック

ThrottledDelayer:
- 呼び出し回数をカウントし、factor回に1回だけdelay()を実行
- `calls = (calls + 1) % factor`

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Cannot yield | yield不可能なコンテキストでsleep呼び出し | errorHandleに通知 |
| - | コルーチンエラー | コルーチン内で例外発生 | errorHandleに通知、スタックトレース出力 |

### リトライ仕様

リトライは行わない。エラー発生時はerrorHandleに通知する。

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

本機能はトランザクションを使用しない。

## パフォーマンス要件

- 各ステップ処理は0.5秒以内に完了することが望ましい
- 0.5秒を超える処理はログに警告を出力（warnStepTime）
- 2秒を超える処理はスタックトレース付きで警告

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

特になし。

## 備考

本機能はLuaの協調的マルチタスク（コルーチン）を基盤としており、プリエンプティブなスレッドとは異なる。タスクは明示的にyieldしない限り実行を継続する。

---

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

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

### 推奨読解順序

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

タスク管理に使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | await.lua | `script/await.lua` | モジュール変数の定義（9-14行目） |

**読解のコツ**:
- `coMap`: コルーチン→タスク情報のマッピング（弱参照）
- `idMap`: ID→コルーチン集合のマッピング（弱参照）
- `delayQueue`: 遅延実行待ちのresume関数キュー
- `needClose`: クローズ待ちのコルーチンリスト

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

主要な関数であるcallとwaitを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | await.lua | `script/await.lua` | call関数（42-66行目）、wait関数（139-153行目） |

**主要処理フロー**:
1. **42-66行目**: call関数 - コルーチン作成とタスク登録
2. **43行目**: コルーチン作成
3. **45-48行目**: coMapへのタスク情報登録
4. **49-55行目**: IDの登録
5. **57-64行目**: 親タスクのclosers継承
6. **65行目**: コルーチン開始

#### Step 3: 待機・遅延処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | await.lua | `script/await.lua` | sleep（119-134行目）、delay（157-177行目） |

**主要処理フロー**:
- **119-134行目**: sleep - タイマーによる待機
- **127行目**: timer.waitでコールバック設定
- **133行目**: coroutine.yieldでサスペンド
- **157-177行目**: delay - 次のステップまで遅延
- **170-175行目**: delayQueueにresume関数を追加

#### Step 4: ステップ実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | await.lua | `script/await.lua` | step関数（229-253行目） |

**主要処理フロー**:
- **230-233行目**: needCloseのコルーチンをクローズ
- **235-244行目**: delayQueueからresume取り出しと実行
- **242-243行目**: 0.5秒超の処理に警告

#### Step 5: キャンセル処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | await.lua | `script/await.lua` | close関数（89-104行目）、setID関数（83-86行目） |

**主要処理フロー**:
- **89-104行目**: close - IDに関連するタスクを一括終了
- **96-97行目**: サスペンド中のコルーチンのみクローズ
- **98-100行目**: クローズコールバックの実行
- **101行目**: coroutine.closeで終了

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

```
proto.applyMethod()
    │
    └─ await.call(callback, id)
           ├─ coroutine.create(callback)
           ├─ setID(id, co)
           │      └─ idMap[id][co] = true
           └─ coroutine.resume(co)
                  │
                  ├─ await.sleep(time)
                  │      ├─ timer.wait(time, resume)
                  │      └─ coroutine.yield()
                  │
                  ├─ await.wait(callback)
                  │      ├─ callback(resume)
                  │      └─ coroutine.yield()
                  │
                  └─ await.delay()
                         ├─ delayQueue追加
                         └─ coroutine.yield()

メインループ:
    └─ await.step()
           ├─ coroutine.close(needClose)
           └─ delayQueue[i]()
                  └─ coroutine.resume(co)
```

### データフロー図

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

callback関数 ───▶ await.call ───▶ コルーチン作成 ───▶ 非同期実行
                        │
                        └───▶ coMap/idMap登録

await.sleep ───▶ timer.wait ───▶ yield ───▶ タイマー発火 ───▶ resume

await.delay ───▶ delayQueue追加 ───▶ yield ───▶ step() ───▶ resume

await.close(id) ───▶ idMap[id]取得 ───▶ coroutine.close ───▶ タスク終了
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| await.lua | `script/await.lua` | ソース | 非同期処理フレームワーク |
| timer.lua | `script/timer.lua` | ソース | タイマー機能（sleep連携） |
| proto.lua | `script/proto/proto.lua` | ソース | LSPハンドラーからの呼び出し元 |
