空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

etolisp 進捗 (23) 〜 ガーベージコレクタの実装 (2)

このまえ(id:xoinu:20080503:1209891180)の続きをごにょごにょ。A garbage collection framework for C++ のコンセプトコードは確かに面白いが,致命的な問題が2つ。1つはそのままのコードだとデストラクタが正しく呼ばれない。node_t のデストラクト時に呼ばれる destroy 関数内で,明示的にデストラクタを呼ぶ処理を入れる必要がある。

template <typename T> class destructor
{
public:
  static void destroy(void *obj)
  {
    static_cast<T*>(obj)->~T();
    operator delete(obj, __eto_gc__);
  }
};

もう1つは,GC 起動のタイミングの問題。

元々のコードだと new(gc) でオブジェクトを作成した後,gc_ptr にそのポインタが代入されて初めて,フレームワークは,そのノードが「使用中」と判断できるようになる。しかし,gc_ptr オブジェクトをメンバとして持つクラスを new(gc) でコンストラクトしている最中にメンバの new(gc) 中にしきい値を超えてゴミ掃除が始まると,まだコンストラクト中であり gc_ptr で管理されていない自分自身は,destroy されてしまう。つまり,new(gc) を呼んで,値が返ってきた時には既に死んでいることがあるという悲しいバグがあった。

結局,このデザインだと,new(gc) の内部でゴミ掃除のトリガーを引くことはできないということだ。そうかといって,適切なゴミ掃除のタイミングを手動で調整するのも面倒くさい。で,妥協に妥協を重ねた結果,ゴミ掃除の判定部分を eto::GC::collectIfNeeded() という関数に括り出し,

#define gcnew new(__eto_gc__)
#define etonew(Construction) gcnew Construction; eto::GC::collectIfNeeded()

みたいなマクロを作って納得することにした。コンストラクト中など,ガベコレが働いて欲しくないセクションでは gcnew を明示的に使い,それ以外では etonew() を使うってルールでどうだろう。将来,ガベコレが呼ばれないパスを作り出しそうで怖いけど。また,etonew(T) は,見た目上単文だけど,実は複文なので,必ず {} で括られたブロック内で利用しなければならない。

if (!pHoge)
  pHoge = etonew(Hoge);

みたいな些細なミスを誘発する悪いデザインであるといえる。さらに,gcnew したポインタを生ポインタで受けても警告も何にも出ないというのは,少し気になるところである。まあそうはいっても,C++的に見てリーズナブルな記法で GC できるようになったことを考えれば,これらの点を差し引いても余りあることだろう。

etolisp のイメージキャラクタ

も,おむつを卒業である。おめでとう*1

*1:いいおむつを手に入れただけか…