非同期例外

id:crimsonwoods:20060608に書いた「マルチスレッド例外」の話。
これは私の造語でどこの誰も(もちろんMicrosoftも)こんな単語は使っていない。
では、「マルチスレッド例外」とは何か。


まずは一般的な例外の話。


C++の例外はthrowで投げられる限り完全に同期です。
また、例外はスタックフレームを飛び越えることはできても
別のスレッドのスタックフレームまでは到達することができません。
スレッドごとにスタックのコンテキストが異なるので、
このような動作になるのは当然です。


同期例外とは別に、世の中には非同期例外というものが存在しており、
任意の1命令を実行している際に例外が発生することがあります。
この場合、予期しない位置で例外が発生するため、
確保済みのリソースが適切に開放されないケースが発生します。
詳しくはid:NyaRuRuさんの以前の記事(id:NyaRuRu:20050515:p5)などが参考になります。
マルチスレッドな環境でこれが発生すると、
サブスレッドのスタックフレームに例外が吸収されてしまい、
例外によるプロセスの強制終了を期待することはできません。
もちろん、スレッドの終了コード判定などである程度回避することは可能です。


つぎに私の「マルチスレッド例外」について。

  • 「マルチスレッド例外」は上述の非同期例外ではありません。
  • 例外がスローされるスレッドはプライマリスレッドではありません。
  • マルチスレッドプログラムでないにも関わらずプライマリスレッド以外のスレッドから例外がスローされることがあります。
  • サブスレッドからスローされた例外により、プロセスが強制終了されることがあります。

さて、ここまで書いてピンと来た方もいると思いますが、
「マルチスレッド例外」の正体はsignalです。
WindowsではCtrl+Cなどの割り込みは例外として扱われると同時に、
signal(SIGINT)として扱われます。
SIGINTが発生すると、システムにより自動的にスレッドが生成され、そのコンテキストで例外がスローされます。
このとき、signalでSIG_IGNとするか、自前のsignalハンドラに置き換えていない限り、プロセスがアボートされます。
この例外はプライマリスレッドでtry{}cath{}しようが__try{}__except{}しようがキャッチすることは不可能です。
唯一signalによるsignalハンドラの書き換えでのみハンドリングを行うことが出来ます。
というわけで、C++の非同期例外とも別モノです。
私はSIGINT以外にこの事例を知りませんが世の中にはこんな例外もあるということで。

(「マルチスレッド例外」とか言わずに最初からsignalって言えという突っ込みがきそう・・・)


その他いろいろ


構造化例外をC++例外に置き換えるための、変換関数をセットする_set_se_translatorですが、
スレッドコンテキストごとに管理されるためマルチスレッド環境ではスレッドごとに_set_se_translatorを
呼び出す必要があります。
また、DLLを用いる場合は、_set_se_translatorがDLL内で呼び出されることで
例外変換関数が上書きされる可能性があります。
例外の奥深くに立ち入る方は十分に注意しましょう(ぉぃ