概要
- RustConf 2025 での非同期Rustにおけるキャンセルについての講演内容の要約
- async Rust におけるキャンセルの仕組みと、その強力さと落とし穴
- キャンセル安全性 と キャンセル正当性 という2つの重要な概念の解説
- 実際のコード例 とともに、問題点とその対策を紹介
- Tokio などの実例を交えつつ、実践的な知見を共有
Rustのasyncキャンセルの本質と落とし穴
- async Rust では、キャンセルは「作業を始めた後でやめる」ことを意味
- ネットワーク通信やファイル読み込み など、長時間かかる処理の中断手段
- 同期Rust では、キャンセルにはフラグ管理やpanic、プロセス終了など複数手法が存在
- フラグ管理 :atomic変数でキャンセル判定
- panic利用 :一部フレームワーク(Salsaなど)で利用、ただしWasm等では非対応
- プロセス・スレッド終了 :安全性やリソース管理の観点からRustでは非推奨
- 共通プロトコル不在 が同期Rustの課題
async Rustでのキャンセルの仕組み
- Future は「状態マシン」としてメモリ上に存在、await/pollされるまで何もしない
- 他言語(Go, JS, C#) ではFuture生成時点で即実行されるが、Rustは「完全な遅延実行」
- キャンセル方法 はFutureをdropするだけ
- 親Futureのキャンセル は 子Futureにも伝播
- 簡単にキャンセルできる 反面、思わぬバグや副作用を生みやすい
キャンセル安全性とキャンセル正当性
- キャンセル安全性(cancel safety) :Futureを途中でキャンセルしても副作用がない性質
- 例: tokio::time::sleep はキャンセル安全
- 例: tokio::sync::mpsc::Sender::send はキャンセル非安全(途中でdropするとメッセージ消失)
- キャンセル正当性(cancel correctness) :システム全体としてキャンセルにより不整合やバグが起きない状態
- キャンセル安全性 はローカルな性質、 キャンセル正当性 はグローバルな性質
- キャンセル非安全なFuture が存在し、それがキャンセルされ、かつシステムの性質を壊す場合にバグとなる
- Tokio Mutex の例:
- ロックの待機中にキャンセル すると、順番待ちの整合性が壊れる危険性
- ドキュメントにも注意書き あり
実際の問題例と対策
- 送信チャンネル(Sender::send) でのタイムアウト実装例
- timeoutでキャンセル すると、送信予定だったメッセージが消失
- システムのキャンセル正当性 を守るための実践的対策
- キャンセル非安全API の利用時は、キャンセルされても副作用がないように設計
- 重要な操作は必ず完了させる、もしくは失敗時のリカバリ処理を実装
- キャンセルの伝播 に注意し、 親子Future間の関係 を意識した設計が必要
まとめと提案
- async Rustのキャンセル は強力だが、落とし穴も多い
- キャンセル安全性と正当性 の両面からシステム設計を見直す重要性
- Tokio等のドキュメント や実例を参考に、 本番運用でのバグ事例 から学ぶ姿勢
- キャンセル非安全なAPI の利用時は、 キャンセル伝播と副作用 を必ず確認
- async Rustの強みを活かしつつ、堅牢な設計・運用 を目指すことが重要