C++20コルーチンからの例外送出ハンドリングに関するメモ。C++コルーチンライブラリの設計者向け。
C++コルーチン言語仕様では、コルーチン送出例外ハンドリングのカスタマイズポイントとしてpromise_type::unhandled_exception
関数を規定する。プログラマが記述したコルーチン本体処理に対して、C++コンパイラは下記のようなコード展開を行う(一部は簡略化)。
{
promise_type _promise;
try {
co_await _promise.initial_suspend();
} catch (...) {
_promise.unhandled_exception();
}
co_await _promise.final_suspend();
}
C++コールチンライブラリ設計者の選択肢としては、3種類の例外ハンドリング戦略が考えられる。
- [A] コルーチンの呼出元(caller)/再開元(resumer)にそのまま例外伝搬させる。ジェネレータ(generator)などの同期型コルーチン向け。
- [B] コルーチンの戻り値型オブジェクトに例外保持しておき、値の読み取りタイミングで例外再スローする。並行タスクや非同期I/Oなどの非同期コルーチン向け。
- [C] 例外送出=プログラム異常終了(
std::terminate
)。
それぞれの例外ハンドリングにおけるpromise_type::unhandled_exception
実装イメージは下記の通り。
void unhandled_exception()
{
throw;
}
void unhandled_exception()
{
ep_ = std::current_exception();
}
void unhandled_exception()
{
std::terminate();
}
選択肢[A]の動作は、C++20統合前のC++コルーチン拡張Coroutine TSに対するP0664R6 Issue#25にて明言されている。
25. Allow unhandled exception escape the user-defined body of the coroutine and give it well defined semantics
The proposed resolution is to eliminate the undefined behavior in the following manner:
- Allow an exception to escape
p.unhandled_exception()
and, in that case, consider the coroutine to be at the final suspend point. Reminder: when a coroutine is at the final suspend point, the coroutine can only be destroyed and a call to member function done()
of the coroutine handle associated with that coroutine returns true
.
- Eliminate possibility of an exception being thrown from evaluation of an expression
co_await p.final_suspend()
by stating that final_suspend
member function of the coroutine promise and await_resume
, await_ready
, and await_suspend
members of the object returned from final_suspend
shall have non-throwing exception specification.
This resolution allows generator implementations to define unhandled_exception
as follows:
void unhandled_exception() { throw; }
With this implementation, if a user of the generator pulls the next value, and during computation of the next value an exception will occur in the user authored body it will be propagate back to the user and the coroutine will be put into a final suspend state and ready to be destroyed when generator destructors is run.
P0664R6 C++ Coroutine TS Issues
関連URL