コルーチンのスレッドはスタックに積まれる

今日はLuaではまってしまいました。各オブジェクトに1つのコルーチンを割り当て、オブジェクトごとにスクリプトを走らせているのですが、オブジェクトの数が一定以上になるとハングアップしてしまうという現象に悩まされました。止まる箇所が不定なので、てっきりメモリの解放し忘れや二重解放なのかと思ってチェックしてみましたが特に問題なし。もしかしてメモリが足りなくなったのかと調べてみましたが全然余裕がありました。
ふと思うところがあり、lua_newthread()の説明を読んでみたらそこに答えがありました。lua_newthread()は、スレッドを作成したあと、戻り値として作成したスレッドを返すのと一緒に、そのスレッドをスタックに積むそうです。そしてスタックから削除されてから初めて、スレッドはガーベジコレクとの対象となるそうです。スレッドがガーベジコレクとの対象となるということは覚えていたのですが、そのタイミングは、一定時間そのスレッドへのアクセスがなくなったときかと思ってました。そんな危険なはずないのに、それで納得していたんですから自分が怖いです。
そんなわけで、コルーチンの作成時、グローバルテーブルにそのスレッドをキーとしてスレッドを登録し、スタック上のコルーチンは削除。コルーチンの終了時に、グローバルテーブルに登録したスレッドを削除。これでうまくいきました。よかったよかった。でも、何故、「エラーで停止」ではなく、「ハングアップ」だったのでしょう? メモリが確保できなくなったらエラーは出るはずなので、もしかするとスタックオーバーフロー? え、上限超えたらエラーとかやってくんないのかな…。
今回のバグ取りで気づいたことは二つ。一つは、メモリの解放チェック機構は必要ということ。今回は、メモリ確保時と解放時にそのアドレスとサイズをログに出力して、そのログをRubyで処理してチェックするという地道なやり方でチェックしましたが、この程度のチェックであればそういう機構として埋め込めるはず。手間の割りに効果的だと思うので、余裕の出来たときに作ってみようと思います。
もう一つは、Luaがかなり頻繁にメモリの確保と解放を行っているということ。luaL_newstate()を使うと単純にrealloc()が使用されるので、ここはちゃんと作らないと今後問題になりそうです。試しに固定ヒープから割り当てるようなアロケータを作ってみたところ、realloc()より遅くなりました。さすがに単純な確保と解放に関してはこっちのほうが早いはずなので、再確保のときのメモリコピーとかが遅いのかなぁ。今はまだいいけど、もう少ししたら真剣に考えないとヤバげです。