Skip to content
2008/11/30 / highmt

GCとか継続とか例外処理とか

Windows上でSEHを使うとき、そのハンドラを指定するエントリは、スタック上にないと動作しない。
(http://www.microsoft.com/msj/0197/exception/exception.aspx)
実際、動的に確保した領域を指定してみたが、スルーされてしまう。
また、そのハンドラ自体も安全であるとマークされたものでないと有効にならない。
(http://msdn.microsoft.com/ja-jp/library/9a89h429(VS.80).aspx)
なので、例外ハンドラを動的に追加するようなコードはなかなか書きにくい。
SetUnhandledExceptionFilter()を使うと動的に例外ハンドラを追加できるが、
こちらは、デバッガがアタッチされているとデバッガに例外がわたってしまい、
期待する動作にならない。(バグと記載されている資料がある一方で、
MSDNのAPIの説明では仕様っぽく書いてある。)
XP以降では、Vectored Exception Handlingというしくみがある。
(http://msdn.microsoft.com/en-us/magazine/cc301714.aspx)
これが求めているものに一番近い。

なぜこんなことを書いているかというと、
GCをコンカレント対応にするために、例外が必要になるから。
GCをコンカレント対応にするためには、ライトバリアが必要で、
Cの世界でそれを(ユーザーにチェックを強いることなく)実現するためには
ページフォルトを利用するぐらいしか手がなさそう。
そうすると、例外ハンドラがどうしても必要になる。
Windowsの世界では、GetWriteWatch()/ResetWriteWatch()という
ライトバリアを実現するためのAPIがあるが、
これは、ある一定期間に書き換えられたページを取得するものなので、
今考えているGCのしくみ(書き換えようとした瞬間に何かをしたい)では、
このAPIは使えない。

で、GCが必要とする例外ハンドラは、まあなんとかして
(スレッドの最初で__try/__exceptを入れるなり、SetUnhandledExceptionFilter()を使うなり、
Vectored Exception Handlingを使うなりして)セットアップはできる。
問題は、この前の部分継続の実装で、これは、例外をまるっきり処理できない。
前回の実装では、reset以降は、もともとのスタックとは別の領域を確保してスタックポインタを差し替えている。
このとき、差し替えたスタック上にある例外ハンドラ登録エントリは無視されているように見える。
実際、reset以降にC++例外を投げてみると、TIBにはきちんと例外ハンドラが登録されているにも
かかわらず、mainを抜けて未処理例外としてあがってくる。
もちろんそれ以前にきちんと例外ハンドラの差し替えとかをやっていないので、
現在の状態では、例外ハンドラを探すときに不正なエントリを見にいく可能性があるし、
もっとそれ以前に、スタックのアンワインドが(特に部分継続を再帰した日には)めちゃくちゃになる気がする。
もともと継続と例外を混ぜる気はないのだが、reset以降はGCできないというのは非常によろしくない。

前回の部分継続の実装では、スタックコピーを減らすためにreset以降の部分しかスタックの保存をやっていないのだけれども、
そいういうわけなので、まじめに、スタックボトムからスタックトップまでを毎回保存・復元するようにしないといけないようだ。
継続はほんとうに便利なものだけれども、こうなってくると、パフォーマンス的にはとても厳しいものがある。

——–
2009/01/12 カテゴリ変更

広告
%d人のブロガーが「いいね」をつけました。