-
Notifications
You must be signed in to change notification settings - Fork 4
GCとコルーチン
Perfume にコルーチンを実装するにあたって、BoehmGC と PCL(Portable Coroutine Library) を組み合わせた際に色々不都合がり、これに対応を行ないました。この時に考えたプログラミング上のテクニックをメモとして公開します。
Perfumeを実装するにあたっては、できる限り汎用のライブラリを使用して実装することにしています。 GC(ガベージコレクション)の実装は BoehmGC を、コルーチンの実装には PCL を使用しています。
当初、コルーチンを実装していてどうしてもうまく動かなくて悩んでいました。
まず、GCの動作について非常に簡単ですが説明したいと思います。(Fig.1)
この構成の場合、メモリが足りなくなりGCが発動すると、「Fig.2 GCの行われ方」のように、
- ヒープ領域のGC
- スタック領域のGC
が実行され、ごみ(どこからも参照されない)と判断されたメモリ領域の回収プロセスが実行されます。 特に2.のスタック領域のGCでは、スタックのトップ(現在のスタックポインタ)からボトムに向かってGCが実行されます。
上に書いたとおり、当初は各コルーチンのスタック領域はヒープ領域に確保していました(PCLのデフォルトの動作)。 そして、このようにすると、GCのタイミングでプロセスが確実に落ちます。 これでしばらくの間悩んでいました。 この間 BoehmGC のソースを眺めたり google を検索したりと原因を突き止めようとしていました。
最終的にたどりついた本現象の原因は「Fig.3 問題発生」のようなことではないかと推測するに至りました。
コルーチンが起動されると、それまでのスタックからコルーチンのスタックに切り替わります。 この時点でGCが発動されると、コルーチンのスタックトップから、もともとのマシンスタックのボトムに向かってGCが行われます。 そうすると、コルーチンのスタックはヒープ領域にとられているため、GCの途中でプロセスがアクセスできない領域(プログラムのテキスト領域とヒープ領域の間に存在する広大なメモリ空間)にアクセスすることになります。 これがプロセスが落ちる原因ということであると推測しました。
そこで、上記問題に対応するようなコードを書き、PCLのスタック領域の配置方法を変更しました。 それが「Fig.4 解決方法」に示す内容です。
つまり、GCがスタックのトップからボトムまでをスキャンするのであれば、これを妨げるものを排除するため、コルーチンのスタック領域をすべてマシンスタックの上に配置することにしました。
また実際には、プロセスが利用できるスタック領域のリソースは有限であるため、これをプログラムで知るための仕組みも作りました。
こうすることで、確かにコルーチンコールからGCが発動されても落ちることはなくなりました。推測は正しかったことになります。
あと、各コルーチンが他のコルーチンのスタックを壊さないよう、スタックの上部 4Kにバリア領域を設定しています。この領域に書き込む(=マシンスタックがオーバーフローする)とプログラムは SIGSEGV により落ちます。他のコルーチンを巻き添えにすることなくプログラムが落ちることになります。本当はそのコルーチンだけ無事に終了させたいのですが、まだ実装できていません。