Open2

WasmLinux: VC++でWasm側からthrowした例外がcatchできない問題

okuokuokuoku

... メモ取ろうとしたら一撃で解決してしまった。。

https://github.com/okuoku/wasmlinux-runner/commit/41ff8b914fd5411291c718ceeb0e7a8f718be86f

extern "C" な関数は、デフォルトで noexcept と見做される。これはドキュメントにもあり、これを避けるには /EHsc- する。そうしない場合、C関数の呼出し中にC++コードから throw した場合(つまり C++ → C → C++ のような呼出しスタックを持つ場合)に、普通の abort と同様のダイアログが出てくることになる。

この辺の事情はMSVCのドキュメントにもちゃんとある。

https://learn.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model

... この手の最適化って /O1 以上じゃないとやらないもんだと思ってたけど、無指定でも容赦なくやってくるようだ。

okuokuokuoku

なぜコレが問題になるのか

WasmLinuxのランタイムでは、スレッドの中断を例外で実現しているため。。つまり、Linuxカーネル側からスレッドの中断がリクエストされた場合、 throw で例外を投げて無理矢理止める実装になっている。このとき、Linuxカーネルはwasm2cされたCコードなので、ランタイム側から見ると extern "C" な関数ということになる。

(↓ のコードは C で実装された関数から呼ばれる)

https://github.com/okuoku/wasmlinux-runner/blob/41ff8b914fd5411291c718ceeb0e7a8f718be86f/hostrunner/runner.cpp#L1059-L1068

もちろん、単に throw だけだとスレッドだけではなくプログラムごと終了してしまうので、スレッドのコードを実行する前に try 節で囲んでいる。

https://github.com/okuoku/wasmlinux-runner/blob/41ff8b914fd5411291c718ceeb0e7a8f718be86f/hostrunner/runner.cpp#L34-L38

https://github.com/okuoku/wasmlinux-runner/blob/41ff8b914fd5411291c718ceeb0e7a8f718be86f/hostrunner/runner.cpp#L500-L509

関数 wasmlinux_user_ctx_exec32extern "C" されている。このため、デフォルトでは例外をthrowしない関数と見做され、try節の中には例外を発生させる要因が無いということになってしまう。このため、コンパイラに /EHsc- オプションを指定して、try節を生存させる必要がある。

/EHsc-/EHs (C++例外をサポートし、 extern "C" な関数も例外をthrowすると見做す) /EHc- (C 関数が例外をthrow しない と見做すオプション /EHc効果を打ち消す) の意味。