sjlj-exceptions
意外と簡単に--enable-sjlj-exceptionsなg++でも動くようになりました。
gccのソースを追ったところ、例外ハンドラに関する情報はスレッド固有領域にある、SjLj_Function_Context構造体で管理されていました。
この構造体は、
struct SjLj_Function_Context { struct SjLj_Function_Context* prev; ... };
というようにシングルリンクリストになっていて、スタックの巻き戻しの際にこのリストを辿るようになっています。
これをファイバに対応させるためには、ファイバ切り替えのタイミングでレジスタと同じように保存/復帰させればよいだろうと考えました。
関連する関数でstaticでないものは次の関数です。
// fcをリストの先頭に追加する // fc->prev には直前の先頭要素が設定される void _Unwind_SjLj_Register(SjLj_Function_Context* fc); // fcをリストの先頭から取り除く // fc->prev が先頭要素になる void _Unwind_SjLj_Unregister(SjLj_Function_Context* fc);
あとはパズルです。
// 現在の先頭要素を取得する SjLj_Function_Context* get_current_sjlj_context() { SjLj_Function_Context dummy; // dummy.prev に先頭要素が設定されて戻ってくる _Unwind_SjLj_Register(&dummy); // dummyを取り除く _Unwind_SjLj_Unregister(&dummy); return dummy.prev; } // 別のリストを設定する void set_sjlj_context_list(SjLj_Function_Context* fc) { SjLj_Function_Context dummy; dummy.prev = fc; // dummy.prev が先頭要素になる _Unwind_SjLj_Unregister(&dummy); }
実際のコードはもう少し手を加えてありますが、基本的な考え方は同じです。
<hamigaki/coroutine/detail/gcc/sjlj_context.hpp>
これでMinGWでは動くようになりましたが、Cygwinがまだ駄目です。
ファイバ自体は動いているようなのですが、終了時に落ちてしまいます。
ここまでやったからにはCygwinでも使えるようにしたいところですが、どうなることやら。