# 通知設計書 23-AlreadyLockedException

## 概要

本ドキュメントは、HH\Lib\File\AlreadyLockedException クラスの設計仕様を定義する。AlreadyLockedExceptionは、ファイルロックの取得を試みた際に、既に他のプロセスやスレッドによってロックされている場合にスローされる例外クラスである。

### 本通知の処理概要

AlreadyLockedExceptionは、ファイルの排他制御（flock）においてノンブロッキングモード（LOCK_NB）でロック取得を試みた際、ファイルが既にロックされている場合に呼び出し元に対してその状態を通知するための例外クラスである。

**業務上の目的・背景**：ファイルベースの排他制御は、複数のプロセスやスレッドが同一ファイルに同時アクセスする際のデータ整合性を保証するために使用される。tryLockx()メソッドはノンブロッキングでロック取得を試み、即座に結果を返す。ロック取得に失敗した場合、呼び出し元は待機せずに代替処理（リトライ、エラー通知、別リソースの使用等）を行うことができる。この例外により、ロック失敗を明確に検出できる。

**通知の送信タイミング**：AlreadyLockedExceptionは以下の状況でスローされる：
- File\Handle::tryLockx(SHARED)で共有ロック取得を試みたが、排他ロックが既に保持されている場合
- File\Handle::tryLockx(EXCLUSIVE)で排他ロック取得を試みたが、共有または排他ロックが既に保持されている場合
- 内部的にflock($fd, LOCK_NB)がEAGAINを返した場合

**通知の受信者**：例外をキャッチする呼び出し元コード（アプリケーション層）が受信者となる。開発者はcatch文でAlreadyLockedExceptionを捕捉し、適切な処理を行う。

**通知内容の概要**：例外オブジェクトとしてスローされる。この例外はOS\ErrnoExceptionを継承しておらず、単純な\Exceptionのサブクラスである。これは、LOCK_NB使用時のEWOULDBLOCKは「エラー」ではなく「期待される動作」であるという設計思想に基づく。

**期待されるアクション**：
- 一定時間後にリトライ
- ブロッキングモードでのlock()メソッド呼び出しへの切り替え
- 代替リソースの使用
- ユーザーへのエラー通知（リソースが使用中である旨）

## 通知種別

例外（Exception） - プログラム実行時のエラー通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（throw文による例外スロー） |
| 優先度 | 中（処理中断だがエラーではない） |
| リトライ | 推奨（短い間隔でリトライ可能） |

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

例外はコールスタックを遡って伝播し、最初にマッチするcatchブロックで捕捉される。捕捉されない場合はプログラムが異常終了する。

## 通知テンプレート

### 例外メッセージ形式

| 項目 | 内容 |
|-----|------|
| フォーマット | デフォルトメッセージ（なし） |
| 例 | N/A（メッセージなし） |

### 本文テンプレート

```
（メッセージなし - 例外クラス名で状態を表現）
```

### 添付ファイル

なし（例外オブジェクトとしてスタックトレースを含む）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | この例外は追加のプロパティを持たない | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | tryLockx(SHARED) | 排他ロック保持中 | 共有ロック取得失敗 |
| API呼び出し | tryLockx(EXCLUSIVE) | 任意のロック保持中 | 排他ロック取得失敗 |
| システム呼び出し | flock($fd, LOCK_NB) | EAGAIN返却 | OSレベルでのロック失敗 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ロック取得成功 | 正常にロックが取得できた場合 |
| ブロッキングモード | lock()メソッド使用時（ブロックして待機） |
| 他のerrno | EAGAIN以外のエラー（OS\ErrnoExceptionがスロー） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[tryLockx呼び出し] --> B[flock LOCK_NB実行]
    B --> C{結果}
    C -->|成功| D[Lockオブジェクト返却]
    C -->|EAGAIN| E[AlreadyLockedException生成]
    C -->|その他エラー| F[BlockingIOException再スロー]
    E --> G[例外スロー]
    G --> H[呼び出し元でキャッチ]
```

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

### 参照テーブル一覧

なし（例外クラスはデータベースを参照しない）

### 更新テーブル一覧

なし（例外クラスはデータベースを更新しない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| AlreadyLockedException | ロック取得失敗（EAGAIN） | リトライまたは代替処理 |
| OS\BlockingIOException | その他のブロッキングエラー | エラー内容に応じた処理 |
| OS\ErrnoException | その他のOSエラー | エラー内容に応じた処理 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | アプリケーション依存（3-5回推奨） |
| リトライ間隔 | 100ms〜1s程度 |
| リトライ対象エラー | AlreadyLockedException |

## 配信設定

### レート制限

なし（例外の発生頻度に制限なし）

### 配信時間帯

なし（例外は発生時即座にスロー）

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

- ファイルパス情報の露出に注意
- ロック競合状態を悪用したDoS攻撃の可能性を考慮
- ロック取得失敗時のリソースリークを防ぐ

## 備考

- AlreadyLockedExceptionはOS\ErrnoExceptionを継承していない（設計上の意図）
- LOCK_NB使用時のEWOULDBLOCK/EAGAINは「エラー」ではなく「期待される結果」
- lock()メソッド（ブロッキング）とtryLockx()メソッド（ノンブロッキング）の使い分けが重要

---

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

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

### 推奨読解順序

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

まず、AlreadyLockedExceptionクラスの構造とLockType列挙型を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AlreadyLockedException.php | `src/file/AlreadyLockedException.php` | 例外クラスの定義（行13-21） |
| 1-2 | Handle.php | `src/file/Handle.php` | tryLockx()メソッドのインターフェース定義（行38-45） |

**読解のコツ**: AlreadyLockedExceptionは\Exceptionを直接継承し、OS\ErrnoExceptionとは無関係である点に注意。これはEAGAINが「エラー」ではなく「期待される動作」であるという設計思想に基づく。

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

tryLockx()メソッドの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CloseableFileHandle.php | `src/file/_Private/CloseableFileHandle.php` | tryLockx()の実装（行56-67） |

**主要処理フロー**:
1. **行57-58**: tryブロック開始、flock呼び出し（LOCK_NB付き）
2. **行59-60**: 成功時はLockオブジェクト返却
3. **行61-64**: BlockingIOExceptionキャッチ、EAGAINならAlreadyLockedException
4. **行65-66**: EAGAIN以外なら元の例外を再スロー

#### Step 3: インターフェースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Handle.php | `src/file/Handle.php` | lock()とtryLockx()の違い（行27-45） |

**主要処理フロー**:
- **行27-34**: lock()はブロッキング、ロック取得まで待機
- **行38-45**: tryLockx()はノンブロッキング、即座に結果返却またはスロー

#### Step 4: テストコードを参照する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | FileTest.php | `tests/file/FileTest.php` | ロックテスト（行160-189） |

**主要処理フロー**:
- **行166-177**: 共有ロック保持中に排他ロック取得を試みてAlreadyLockedException
- **行181-188**: 排他ロック保持中に共有ロック取得を試みてAlreadyLockedException

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

```
アプリケーションコード
    │
    ├─ File\Handle::tryLockx(LockType)
    │      │
    │      └─ CloseableFileHandle::tryLockx()
    │             │
    │             ├─ OS\flock($impl, $type | LOCK_NB)
    │             │      │
    │             │      ├─ 成功：Lock返却
    │             │      │
    │             │      └─ 失敗：BlockingIOException
    │             │
    │             └─ catch (BlockingIOException)
    │                    │
    │                    ├─ EAGAIN → AlreadyLockedException
    │                    │
    │                    └─ その他 → 再スロー
    │
    └─ 呼び出し元でAlreadyLockedExceptionをcatch
```

### データフロー図

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

tryLockx(LockType) ───▶ flock(LOCK_NB)呼び出し
                              │
                              ├─▶ 成功 ───▶ Lock オブジェクト
                              │
                              └─▶ 失敗（EAGAIN） ───▶ AlreadyLockedException
                              │
                              └─▶ 失敗（その他） ───▶ BlockingIOException/ErrnoException
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AlreadyLockedException.php | `src/file/AlreadyLockedException.php` | ソース | 例外クラス定義 |
| Handle.php | `src/file/Handle.php` | ソース | tryLockx()インターフェース定義 |
| CloseableFileHandle.php | `src/file/_Private/CloseableFileHandle.php` | ソース | tryLockx()実装 |
| FileTest.php | `tests/file/FileTest.php` | テスト | ロックテストケース |
| ErrnoException.php | `src/os/ErrnoException.php` | ソース | BlockingIOException定義（参照） |
