今回の要約: とりあえずGC_oom_fn対策のコードだけ書いた。動作確認とかは帰ってからやる。


今日は、出社する事になった。


先日の優先順位を考えた。

  1. とりあえず、GC_oom_fn対策が実現可能な事を簡単に確認する
  2. とりあえず適当に動く、eval/svを作る(大体完了)
  3. とりあえず適当に動く、eval/memlimitを作る(大体完了)
  4. とりあえず適当に動く、「安全なeval」が仮完成
  5. とりあえず適当に動く、非同期入出力のキュー(圧縮機能/優先順位制御機能付き)を作る
  6. とりあえず適当に動く、コードジェネレータ用の環境を作る
  7. とりあえず適当に動く、コードジェネレータ用の入出力ドライバを作る
  8. とりあえず適当に動く、コードジェネレータ用の評価器を作る
  9. とりあえず適当に動く、コードジェネレータが仮完成
  10. eval/svを完璧かつ高速で動作するように直す
  11. GC_oom_fn対策を実装する
  12. eval/memlimitを完璧かつ高速で動作するように直す
  13. 「安全なeval」を完璧にする
  14. 非同期入出力のキュー(圧縮機能/優先順位制御機能付き)を完璧にする
  15. コードジェネレータ用の環境を完璧にする
  16. コードジェネレータ用の入出力ドライバを完璧にする
  17. コードジェネレータ用の評価器を完璧にする
  18. 完全なコードジェネレータが完成


GC_oom_fn対策は必要不可欠だが、実は、実装可能な事だけ確認したら、後回しにしても良さそうな事が分かった。
しかし、折角なので、この段階で実装してみる事にする。


実装方針は、以下の方針とする。

  • 変更部分は#ifdef GAUCHE_EXPERIMENTAL_GC_OOM_FNで囲んで、フラグを与えない場合は元のGaucheとしてコンパイルできるようにする
  • ちょっとずつ変更しては確認検証を行い、インクリメンタル開発をする


実装手順は、以下の順序とする。

  1. Scm_Init()内で、OOM用予備領域を確保するように変更する
  2. oom_handler()で、OOM用予備領域を解放再利用してからScm_Error()を投げるように変更する
  3. SCM_MALLOC系マクロを、OOM用予備領域のポインタがNULLになっていたら再確保するように変更する
    • OOM用予備領域を使った後、どのタイミングでOOM用予備領域を再確保するか悩んだが、とりあえずSCM_MALLOC系のタイミングにする事にした。
    • 再確保する条件は、仮に、元のOOM用予備領域のサイズの二倍の空き領域が確認されたら、という事にする。


とりあえず、このコードは簡単に書いてみた。
コンパイル&実行テスト&全コードのSCM_MALLOCやSCM_NEW等の確認は、帰ってきてからする。

  • gauche/memory.h で、素のGC_MALLOC_WORDSを呼んだりしてるのを発見した。
    • とりあえずSCM_INLINE_MALLOC_PRIMITIVESは一時的に0にする事にした。ちゃんと動くのを確認してから対策を考える。
  • vm.c で、以下の関数が呼ばれている。これらがmalloc類に相当するか調べて、malloc類っぽかったら、それらしく対応する事。

この辺は家に帰ってきてから自分で調べます。


ところで、oom_handler()が、本当にあらゆる場面でScm_Error()を投げて安全だったとしても、「本当にエラー例外を投げるだけでいいのか」という問題がある。

  • 単にエラー例外を投げるだけの場合、「単純なconsですらエラー例外が発生する可能性が常にある」という事で、厳密なコードを書く場合、ほぼあらゆる場面でエラー例外が発生する可能性を考えなくてはならなくなる
    • 現実的には、そこまで考えなくてもまず大丈夫だとは思うけれど
  • じゃあ、どうすればいいのかと言うと、「途中でOOMが発生したevalは信用ならないので、もう途中で中断する」という扱いにするのが妥当?
  • ok。じゃあ、Scm_Eval直後で、脱出用継続を保持しといて、oom_handler()からそれを辿る事にしよう。
  • ところで、OOMが発生する直前とかに、eval内からeval外の変数に継続をset!されてたら、せっかく抜けても、またevalが再実行されそうなんだけど?
  • それと、継続を使って抜ける場合は、dynamic-windのafter thunkが実行される気がするけど、いいの?
    • 仮に無理矢理dynamic-windのafter thunkを実行せずに抜けたとして、Scheme的にはそれでいいの?
      • 例えば、さっきの、eval外の変数にset!された継続を辿って再突入した場合、after thunkは実行されてないのに再度before thunkは実行される事になるけど、これって、beforeとafterの対応が取れなくなるよね?

等と、考えた結果、やっぱりエラー例外しかないように思えてきた。
そもそも、OOMのScheme的セマンティクスって、どんななんだろう。
とりあえず無限エクステントと相反してる気はする……。
そう考えると、現在のGaucheの「いきなりScm_Panic()で終了」というのは、かなり正しいように思える。
思えるが、それだと「安全なeval」は実現できない……。
Scm_Cleanup()を呼ぶのもアリそうだけど、これもやっぱり「安全なeval」には使えなさそうだ。


とりあえず、帰ったら動くかどうか試そう。