# 機能設計書 50-非同期イベント

## 概要

Julia Base ライブラリにおける非同期イベント通知（AsyncCondition, Timer, sleep, timedwait）を提供する機能の設計書である。

### 本機能の処理概要

**業務上の目的・背景**：非同期プログラミングでは、外部イベント（C コードからの通知、時間経過）に応じてタスクを起床させる仕組みが必要である。本機能は libuv のイベントループと統合された非同期イベント通知機構を提供し、タイマーベースの処理スケジューリングや C コードからの Julia タスクへの通知を可能にする。

**機能の利用シーン**：定期的なポーリング処理（Timer with interval）、一定時間後の処理実行（Timer with timeout）、スリープ処理（sleep）、タイムアウト付き待機（timedwait）、C 拡張からの Julia タスクへの非同期通知（AsyncCondition）、定期的なステータスチェックやハートビート処理など。

**主要な処理内容**：
1. `AsyncCondition` による C コードからの非同期通知受信
2. `AsyncCondition(callback)` によるコールバック付き非同期通知
3. `Timer(delay; interval)` によるタイマーの作成
4. `Timer(callback, delay; interval)` によるコールバック付きタイマー
5. `sleep(seconds)` による現在タスクのスリープ
6. `timedwait(testcb, timeout; pollint)` によるタイムアウト付きポーリング待機
7. `wait(t)` / `close(t)` / `isopen(t)` による Timer/AsyncCondition の操作

**関連システム・外部連携**：libuv イベントループ（`uv_async_init`, `uv_timer_init`, `uv_timer_start` 等）、C ランタイム（`uv_async_send` による外部通知）と連携する。

**権限による制御**：非同期イベントの使用に権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | CLI / REPL | 主画面 | sleep/Timer の対話的実行 |

## 機能種別

非同期処理 / イベント駆動

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| delay / timeout | Real | Yes (Timer) | タイマーの初期遅延（秒） | >= 0 |
| interval | Real | No | 繰り返し間隔（秒、デフォルト: 0 = 一回のみ） | >= 0 |
| callback | Function | No | タイマー/AsyncCondition のコールバック関数 | Timer/AsyncCondition を引数に取る |
| seconds | Real | Yes (sleep) | スリープ秒数 | >= 0 |
| testcb | Function | Yes (timedwait) | ポーリングテスト関数（Bool を返す） | - |
| pollint | Real | No (timedwait) | ポーリング間隔（秒、デフォルト: 0.1） | >= 0.001 |
| spawn | Union{Nothing,Bool} | No (Timer callback) | タスクを別スレッドで実行するか | - |

### 入力データソース

ユーザーコードから直接引数として渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| timer | Timer | 作成されたタイマーオブジェクト |
| async | AsyncCondition | 作成された非同期条件オブジェクト |
| result | Symbol | timedwait の結果（:ok / :timed_out） |
| - | Nothing | sleep の戻り値（なし） |

### 出力先

関数の戻り値としてユーザーコードに返される。

## 処理フロー

### 処理シーケンス

```
1. AsyncCondition の場合:
   1-1. uv_async_init でイベントループにハンドル登録
   1-2. wait 呼出でタスク suspend
   1-3. C からの uv_async_send でコールバック uv_asynccb が呼ばれる
   1-4. set=true に設定し cond に notify
   1-5. wait していたタスクが起床

2. Timer の場合:
   2-1. uv_timer_init + uv_timer_start でタイマー開始
   2-2. wait 呼出でタスク suspend
   2-3. timeout 経過で uv_timercb が呼ばれる
   2-4. set=true に設定し cond に notify
   2-5. interval > 0 の場合は繰り返し
   2-6. interval == 0 の場合はタイマー停止 + close

3. sleep の場合:
   3-1. Timer(sec) を作成
   3-2. wait(timer) でタイマー完了を待つ

4. timedwait の場合:
   4-1. testcb() が true なら即座に :ok を返す
   4-2. pollint 間隔のタイマーを作成
   4-3. ポーリングループ: testcb() チェック → true なら :ok
   4-4. timeout 超過なら :timed_out
```

### フローチャート

```mermaid
flowchart TD
    A[非同期イベント要求] --> B{種別}
    B -->|AsyncCondition| C[uv_async_init]
    C --> D[wait で suspend]
    D --> E[uv_async_send 受信]
    E --> F[set=true + notify]
    F --> G{コールバック?}
    G -->|Yes| H[callback 実行]
    H --> D
    G -->|No| I[タスク起床]
    B -->|Timer| J[uv_timer_init + start]
    J --> K[wait で suspend]
    K --> L[timeout 経過]
    L --> M[set=true + notify]
    M --> N{interval > 0?}
    N -->|Yes| O[繰り返し]
    O --> K
    N -->|No| P[close]
    B -->|sleep| Q[Timer(sec)]
    Q --> R[wait(timer)]
    B -->|timedwait| S{testcb() == true?}
    S -->|Yes| T[:ok]
    S -->|No| U[Timer(pollint, interval=pollint)]
    U --> V{testcb()?}
    V -->|Yes| W[close timer + :ok]
    V -->|No| X{timeout?}
    X -->|Yes| Y[close timer + :timed_out]
    X -->|No| V
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-50-01 | Timer の +1ms 補正 | libuv が 1ms 早くタイムアウトする傾向があるため、timeout に +1ms を追加 | Timer 作成時（timeout > 0） |
| BR-50-02 | interval=0 の一回性 | interval=0（デフォルト）のタイマーは一度だけ発火し自動クローズ | Timer(delay) |
| BR-50-03 | close 後の wait | クローズ済みの Timer/AsyncCondition への wait は EOFError | close 後 |
| BR-50-04 | timedwait の最小ポーリング間隔 | pollint は最低 1ms (0.001秒) | timedwait |
| BR-50-05 | sleep の最小スリープ時間 | sleep の最小値は 1ms | sleep 呼出時 |
| BR-50-06 | Timer の accumulating skew | interval タイマーは累積的な時間ずれが発生し得る | 高精度タイミングが必要な場合 |
| BR-50-07 | AsyncCondition のメモリオーダリング | AsyncCondition は送信スレッドと受信スレッド間で acquire/release メモリオーダリングを提供 | スレッド間通信時 |
| BR-50-08 | Timer の spawn オプション | spawn=true で別スレッドでコールバック実行、spawn=nothing（デフォルト）は親タスクの sticky に依存 | Timer(cb, ...) |

### 計算ロジック

タイマーのタイムアウト値はミリ秒単位で `ceil(UInt64, timeout * 1000) + !iszero(timeout)` として計算される。+1 は libuv の 1ms 早期タイムアウトの補正。interval は `ceil(UInt64, interval * 1000)` で計算される。

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

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EOFError | Exception | クローズ済みオブジェクトへの wait | isopen で事前チェック |
| ArgumentError | ArgumentError | 負の timeout / interval | 0 以上の値を指定 |
| ArgumentError | ArgumentError | pollint < 0.001 | 0.001 以上の値を指定 |
| _UVError | Exception | uv_async_init の失敗（内部エラー） | - |

### リトライ仕様

自動リトライは行われない。タイマーのコールバック内で例外が発生した場合、エラーは stderr に出力されタイマーが停止する。

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

該当なし。非同期イベントは単方向の通知であり、トランザクション性はない。

## パフォーマンス要件

- sleep の最小分解能は 1ms
- Timer の精度は libuv（およびOS）のタイマー精度に依存する（通常 1-15ms 程度）
- AsyncCondition の通知はマイクロ秒オーダーで処理される
- timedwait のポーリングは pollint 間隔で testcb を呼び出すため、高頻度ポーリングは CPU 負荷を増加させる

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

- AsyncCondition は C コードからの通知を受け付けるため、不正な C 拡張からの予期しない通知に注意
- Timer のコールバックは任意の Julia コードを実行するため、コールバック内のセキュリティに注意

## 備考

- Timer は `t.timeout` と `t.interval` プロパティで設定値を読み取り可能（Julia 1.12 以降）
- `_trywait` は Timer と AsyncCondition で共有される内部関数で、set フラグの確認とリセットを行う
- `uvfinalize` は GC のファイナライザとして、オブジェクト回収時に libuv ハンドルを適切に解放する
- Timer/AsyncCondition の `handle` は libuv のメモリを直接管理するため、Libc.malloc/free で手動管理される

---

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

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

### 推奨読解順序

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

AsyncCondition と Timer の内部構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | asyncevent.jl | `base/asyncevent.jl` | **17-39行目**: `AsyncCondition` 構造体。`@atomic handle`（Ptr{Cvoid}、libuv ハンドル）、`cond`（ThreadSynchronizer）、`@atomic isopen`、`@atomic set`。コンストラクタで `uv_async_init` を呼び出し、finalizer を登録 |
| 1-2 | asyncevent.jl | `base/asyncevent.jl` | **109-139行目**: `Timer` 構造体。AsyncCondition と同様のフィールドに加えて `timeout_ms` と `interval_ms`。コンストラクタで `uv_timer_init` + `uv_timer_start` を呼び出す |

**読解のコツ**: 両構造体とも libuv のハンドルを直接管理する。`iolock_begin()` / `iolock_end()` は I/O ロック（グローバルイベントループロック）のスコープを示す。`@cfunction` は Julia 関数を C コールバックとして渡すためのマクロ。

#### Step 2: イベント待機機構を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | asyncevent.jl | `base/asyncevent.jl` | **164-200行目**: `_trywait(t)` - Timer/AsyncCondition 共通の待機関数。set フラグ確認 → iolock 取得 → preserve_handle → cond.wait → set リセット |
| 2-2 | asyncevent.jl | `base/asyncevent.jl` | **202-205行目**: `wait(t)` - `_trywait` のラッパー。false なら EOFError |

**主要処理フロー**:
1. **164-166行目**: set フラグが true なら即座にリターン（AsyncCondition はメモリフェンス付き）
2. **170-176行目**: isopen でない場合、close を呼んで false を返す
3. **177-196行目**: iolock 取得 → set 再確認 → handle 保護 → cond lock → cond.wait → unlock
4. **198行目**: `@atomic :monotonic t.set = false` でフラグリセット

#### Step 3: libuv コールバックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | asyncevent.jl | `base/asyncevent.jl` | **282-292行目**: `uv_asynccb(handle)` - liuv からの非同期通知コールバック。cond lock → set=true → notify |
| 3-2 | asyncevent.jl | `base/asyncevent.jl` | **294-311行目**: `uv_timercb(handle)` - libuv からのタイマーコールバック。set=true → repeat==0 なら close → notify |

#### Step 4: 高レベル関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | asyncevent.jl | `base/asyncevent.jl` | **319-323行目**: `sleep(sec)` - Timer(sec) を作成して wait するだけのシンプルな実装 |
| 4-2 | asyncevent.jl | `base/asyncevent.jl` | **363-390行目**: `Timer(cb, timeout; spawn, kwargs...)` - コールバック付きタイマー。Task を作成して _trywait ループで cb を繰り返し実行 |
| 4-3 | asyncevent.jl | `base/asyncevent.jl` | **414-432行目**: `timedwait(testcb, timeout; pollint)` - pollint 間隔の Timer でポーリング。testcb() が true になるか timeout 超過で終了 |

#### Step 5: クローズとファイナライズを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | asyncevent.jl | `base/asyncevent.jl` | **218-246行目**: `close(t)` - isopen=false → jl_close_uv → handle が NULL になるまで wait |
| 5-2 | asyncevent.jl | `base/asyncevent.jl` | **248-266行目**: `uvfinalize(t)` - GC ファイナライザ。disassociate_julia_struct → jl_close_uv → handle=NULL → notify |
| 5-3 | asyncevent.jl | `base/asyncevent.jl` | **268-280行目**: `_uv_hook_close(t)` - libuv の close 完了コールバック。handle 解放 → notify |

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

```
Timer(timeout; interval)
    ├─ Libc.malloc(_sizeof_uv_timer)      ... ハンドルメモリ確保
    ├─ associate_julia_struct(handle, this)
    ├─ uv_timer_init(loop, handle)         ... libuv 初期化
    ├─ finalizer(uvfinalize, this)         ... GC ファイナライザ登録
    └─ uv_timer_start(handle, uv_timercb, ms, interval_ms)

wait(t::Timer)
    └─ _trywait(t)
        ├─ t.set == true ? return true
        ├─ !isopen(t) ? close(t); return false
        └─ iolock_begin → preserve_handle → lock(t.cond) → wait(t.cond)

uv_timercb(handle)  [liuv から呼ばれる]
    ├─ lock(t.cond)
    ├─ @atomic t.set = true
    ├─ repeat==0 ? close(t) ... 一回限りのタイマー
    └─ notify(t.cond, true)

sleep(sec)
    ├─ Timer(sec)
    └─ wait(timer)

Timer(cb, timeout; spawn, kwargs...)
    ├─ Timer(timeout; kwargs...)
    └─ @task while _trywait(timer)
           ├─ cb(timer)
           └─ isopen(timer) || return

timedwait(testcb, timeout; pollint)
    ├─ testcb() ? return :ok
    ├─ Timer(pollint, interval=pollint)
    └─ while _trywait(t)
           ├─ testcb() ? close(t); return :ok
           └─ timeout? ? close(t); return :timed_out

close(t)
    ├─ @atomic isopen = false
    ├─ ccall(:jl_close_uv, ...)
    └─ while handle != C_NULL: wait(t.cond)
```

### データフロー図

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

timeout (秒) ──────▶ Timer ──▶ uv_timer_start      ──────▶ Timer オブジェクト
                              ▼ (時間経過)
                     uv_timercb ──▶ set=true + notify ──────▶ 待機タスク起床

(C code) ──────────▶ uv_async_send                   ──────▶ uv_asynccb
                     uv_asynccb ──▶ set=true + notify ──────▶ 待機タスク起床

seconds ───────────▶ sleep ──▶ Timer(sec) + wait     ──────▶ (nothing)

testcb + timeout ──▶ timedwait ──▶ pollint Timer     ──────▶ :ok / :timed_out
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| asyncevent.jl | `base/asyncevent.jl` | ソース | AsyncCondition, Timer, sleep, timedwait の定義 |
| condition.jl | `base/condition.jl` | ソース | GenericCondition（内部の ThreadSynchronizer の基盤） |
| lock.jl | `base/lock.jl` | ソース | ThreadSynchronizer = GenericCondition{SpinLock} の定義元 |
| locks-mt.jl | `base/locks-mt.jl` | ソース | SpinLock（ThreadSynchronizer 内部のロック） |
| task.jl | `base/task.jl` | ソース | Task（コールバック実行の基盤） |
| libuv.jl | `base/libuv.jl` | ソース | libuv 統合の基盤（eventloop, iolock_begin/end 等） |
