# 機能設計書 88-ガベージコレクション管理

## 概要

本ドキュメントは、lua-language-serverにおけるガベージコレクション管理機能について記載する。この機能は、リソースの自動解放（デストラクタパターン）を実現するGCコンテナを提供する。

### 本機能の処理概要

**業務上の目的・背景**：Language Serverでは、ファイルハンドル、タイマー、コルーチンなど、明示的な解放が必要なリソースを多数扱う。これらのリソースを適切に管理しないと、メモリリークやリソースリークが発生する。本機能は、複数のリソース（析構器）を1つのコンテナにまとめ、コンテナの削除時に一括で解放する仕組みを提供する。Luaにはデストラクタがないため、本機能でリソースのライフサイクル管理を代替する。

**機能の利用シーン**：LSPリクエスト処理時のリソース管理、ワークスペースのファイル監視登録、タイマー管理、非同期処理のクリーンアップなど、一時的なリソースを管理する必要があるすべての場面で利用される。

**主要な処理内容**：
1. GCコンテナの作成
2. 析構器（オブジェクトまたは関数）の追加
3. コンテナ削除時の一括解放
4. 既に削除されたオブジェクトのスキップ（圧縮処理）
5. オブジェクトとファンクション両方の析構パターンサポート

**関連システム・外部連携**：utilityモジュールのdump関数をデバッグに使用。ログモジュール（log.lua）でエラーを記録。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面を持たず、リソース管理基盤として動作 |

## 機能種別

ユーティリティ / リソース管理

## 入力仕様

### 入力パラメータ

#### gc()コンストラクタ

パラメータなし。空のGCコンテナを作成する。

#### gc:add関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| obj | any | Yes | 追加する析構器（オブジェクトまたは関数） | tableまたはfunction |

### 入力データソース

- 各モジュールからのリソース登録

## 出力仕様

### 出力データ

#### gc()コンストラクタ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| gc | table | GCコンテナオブジェクト |

#### gc:add関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| obj | any | 追加されたオブジェクト（チェーン用） |

### 出力先

- 呼び出し元へのGCコンテナ返却

## 処理フロー

### 処理シーケンス

```
gc()作成処理:
1. 空のリストを持つGCオブジェクトを作成
   └─ setmetatable({ _list = {} }, mt)
2. GCオブジェクトを返却

gc:add処理:
1. コンテナが既に削除済みかチェック
   └─ self._removedがtrueなら即座に解放
2. リストにオブジェクトを追加
   └─ self._list[#self._list+1] = obj
3. リストサイズが_maxを超えたら圧縮
   └─ zip(self)
4. 追加したオブジェクトを返却

gc:remove処理:
1. 既に削除済みなら何もしない
   └─ self._removedがtrueならreturn
2. 削除済みフラグを設定
   └─ self._removed = true
3. リスト内の全オブジェクトを解放
   └─ destroyGCObject(list[i])

圧縮処理（zip）:
1. リストを走査
2. 削除済みオブジェクトを検出
   └─ isRemoved(obj)
3. 末尾の要素で置換
4. _maxを再計算
   └─ _max = #list * 1.5
```

### フローチャート

```mermaid
flowchart TD
    subgraph create["gc()作成"]
        C1[空のリスト作成] --> C2[メタテーブル設定]
        C2 --> C3[GCオブジェクト返却]
    end

    subgraph add["gc:add処理"]
        A1[オブジェクト受け取り] --> A2{_removed?}
        A2 -->|Yes| A3[destroyGCObject]
        A3 --> A4[nil返却]
        A2 -->|No| A5[リストに追加]
        A5 --> A6{#list > _max?}
        A6 -->|Yes| A7[zip実行]
        A6 -->|No| A8[オブジェクト返却]
        A7 --> A8
    end

    subgraph remove["gc:remove処理"]
        R1[remove呼び出し] --> R2{_removed?}
        R2 -->|Yes| R3[即リターン]
        R2 -->|No| R4[_removed = true]
        R4 --> R5[リスト走査]
        R5 --> R6[destroyGCObject]
        R6 --> R7{次の要素?}
        R7 -->|Yes| R6
        R7 -->|No| R8[完了]
    end

    subgraph destroy["destroyGCObject処理"]
        D1[オブジェクト受け取り] --> D2{type?}
        D2 -->|function| D3[xpcall実行]
        D2 -->|table| D4{remove method?}
        D4 -->|Yes| D5[obj:remove()]
        D4 -->|No| D6[スキップ]
    end

    subgraph zip["圧縮処理"]
        Z1[リスト走査] --> Z2{isRemoved?}
        Z2 -->|Yes| Z3[末尾で置換]
        Z2 -->|No| Z4[index++]
        Z3 --> Z5{次の要素?}
        Z4 --> Z5
        Z5 -->|Yes| Z1
        Z5 -->|No| Z6[_max再計算]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-88-01 | オブジェクト析構 | tableのremove()メソッドを呼び出す | type(obj) == "table" |
| BR-88-02 | 関数析構 | 関数を直接呼び出す | type(obj) == "function" |
| BR-88-03 | 削除済みスキップ | _removedフラグまたはremovedアップバリューで判定 | 圧縮処理時 |
| BR-88-04 | 自動圧縮 | リストサイズが_maxを超えたら圧縮 | gc:add呼び出し時 |
| BR-88-05 | 最小_max | _maxは最低10 | 圧縮処理後 |
| BR-88-06 | 即時解放 | 削除済みコンテナへの追加は即座に解放 | gc:add呼び出し時 |

### 計算ロジック

_maxの計算:
```lua
self._max = #list * 1.5
if self._max < 10 then
    self._max = 10
end
```

### 析構パターン

1. **オブジェクト型析構器**:
   - `obj:remove()` メソッドで析構
   - `obj._removed` フラグで削除済み判定

2. **関数型析構器**:
   - 関数を直接呼び出して析構
   - 上値 `removed` で削除済み判定

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 析構エラー | 析構処理中に例外発生 | xpcallでキャッチしlog.errorで記録、処理を継続 |
| - | removed上値なし | 関数型析構器にremoved上値がない | 警告ログを出力 |

### リトライ仕様

リトライは行わない。エラー発生時はログに記録して処理を継続する。

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

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

## パフォーマンス要件

- 圧縮処理は_maxを超えた場合のみ実行してオーバーヘッドを最小化
- 削除済みオブジェクトの検出は効率的に行う

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

特になし。

## 備考

本機能はLuaにおけるRAII（Resource Acquisition Is Initialization）パターンの実現を目指している。Lua 5.4以降のto-be-closed変数（<close>属性）との併用も想定されている。

---

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

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

### 推奨読解順序

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

GCコンテナの構造とメタテーブルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | gc.lua | `script/gc.lua` | メタテーブル定義（3-11行目） |

**読解のコツ**:
- `mt`: GCコンテナのメタテーブル
- `mt._list`: 析構器のリスト
- `mt._removed`: 削除済みフラグ
- `mt._max`: 圧縮閾値（初期値10）

#### Step 2: 析構関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | gc.lua | `script/gc.lua` | destroyGCObject関数（13-23行目） |

**主要処理フロー**:
- **13行目**: 型判定開始
- **14-16行目**: function型の場合、xpcallで呼び出し
- **17-22行目**: table型の場合、removeメソッドを呼び出し

#### Step 3: 削除済み判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | gc.lua | `script/gc.lua` | isRemoved関数（25-49行目） |

**主要処理フロー**:
- **26-42行目**: function型の場合、debug.getupvalueでremoved上値を探索
- **43-47行目**: table型の場合、_removedフラグをチェック

#### Step 4: 圧縮処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | gc.lua | `script/gc.lua` | zip関数（51-73行目） |

**主要処理フロー**:
- **52-53行目**: リストとインデックス初期化
- **54-68行目**: 削除済みオブジェクトを末尾で置換
- **69-72行目**: _max再計算

#### Step 5: メインAPI（remove, add）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | gc.lua | `script/gc.lua` | remove関数（75-84行目）、add関数（91-101行目） |

**主要処理フロー**:
- **75-84行目**: remove - 全オブジェクトの解放
- **91-101行目**: add - オブジェクト追加と圧縮

#### Step 6: コンストラクタを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | gc.lua | `script/gc.lua` | モジュール返却（117-121行目） |

**主要処理フロー**:
- **117-121行目**: コンストラクタ関数を返却

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

```
local gc = require 'gc'
    │
    └─ gc() → GCコンテナ作成
           └─ setmetatable({ _list = {} }, mt)

gc:add(obj)
    │
    ├─ [_removed] destroyGCObject(obj) → nil
    │
    └─ [not _removed]
           ├─ _list[#_list+1] = obj
           └─ [#list > _max] zip(self)
                  └─ isRemoved(obj)
                         ├─ [function] debug.getupvalue → removed
                         └─ [table] obj._removed

gc:remove()
    │
    ├─ self._removed = true
    └─ for i = 1, #list do
           └─ destroyGCObject(list[i])
                  ├─ [function] xpcall(obj, log.error)
                  └─ [table] xpcall(remove, log.error, obj)
```

### データフロー図

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

gc()
    │
    └───▶ setmetatable ───▶ GCコンテナ

gc:add(obj)
    │
    └───▶ _list追加 ───▶ [size > _max] zip ───▶ obj返却

gc:remove()
    │
    └───▶ destroyGCObject ───▶ 各析構器を実行
              │
              ├─ [function] → 直接呼び出し
              └─ [table] → obj:remove()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| gc.lua | `script/gc.lua` | ソース | GC管理機能の実装 |
| utility.lua | `script/utility.lua` | ソース | dumpユーティリティ |
| log.lua | `script/log.lua` | ソース | エラーログ出力 |
