llm_worker_rs/docs/spec/cancellation.md

91 lines
3.0 KiB
Markdown

# 非同期キャンセル設計
Workerの非同期キャンセル機構についての設計ドキュメント。
## 概要
`tokio_util::sync::CancellationToken`を用いて、別タスクからWorkerの実行を安全にキャンセルできる。
```rust
let worker = Arc::new(Mutex::new(Worker::new(client)));
// 実行タスク
let w = worker.clone();
let handle = tokio::spawn(async move {
w.lock().await.run("prompt").await
});
// キャンセル
worker.lock().await.cancel();
```
## キャンセルポイント
キャンセルは以下のタイミングでチェックされる:
1. **ターンループ先頭**`is_cancelled()`で即座にチェック
2. **ストリーム開始前**`client.stream()`呼び出し時
3. **ストリーム受信中**`tokio::select!`で各イベント受信と並行監視
4. **ツール実行中**`join_all()`と並行監視
## キャンセル時の処理フロー
```
キャンセル検知
timeline.abort_current_block() // 進行中ブロックの終端処理
run_on_abort_hooks("Cancelled") // on_abort フック呼び出し
Err(WorkerError::Cancelled) // エラー返却
```
## API
| メソッド | 説明 |
| ---------------------- | --------------------------------------------------------- |
| `cancel()` | キャンセルをトリガー |
| `is_cancelled()` | キャンセル状態を確認 |
| `cancellation_token()` | トークンへの参照を取得(`clone()`してタスク間で共有可能) |
## on_abort フック
`Hook::on_abort(&self, reason: &str)`がキャンセル時に呼ばれる。
クリーンアップ処理やログ記録に使用できる。
```rust
async fn on_abort(&self, reason: &str) -> Result<(), HookError> {
log::info!("Aborted: {}", reason);
Ok(())
}
```
呼び出しタイミング:
- `WorkerError::Cancelled` — reason: `"Cancelled"`
- `ControlFlow::Abort(reason)` — reason: フックが指定した理由
---
## 既知の問題
### 1. キャンセルトークンの再利用不可
`CancellationToken`は一度キャンセルされると永続的にキャンセル状態になる。
同じWorkerインスタンスで再度`run()`を呼ぶと即座に`Cancelled`エラーになる。
**対応案:**
- `run()`開始時に新しいトークンを生成する
- `reset_cancellation()`メソッドを提供する
### 2. Sync バウンドの追加(破壊的変更)
`tokio::select!`使用のため、Handler/Scope型に`Sync`バウンドを追加した。
既存のユーザーコードで`Sync`未実装の型を使用している場合、コンパイルエラーになる。
### 3. エラー時のon_abort呼び出し
現在、`on_abort`はキャンセルとフックAbort時のみ呼ばれる。
ストリームエラー等のその他エラー時には呼ばれないため、一貫性に欠ける可能性がある。