Giraffe+ 0.5.52.666

Downloadページへ

前リリースからの変化

  • Boostを1.36.0に更新
  • 値プロパティの無駄なメソッド化を廃止

Boostを1.36.0に更新

functionの変更:
アロケータが、テンプレートではなくコンストラクタの引数で渡す仕様になりました。ほとんどの人には無関係な仕様変更なんですが、Boehm GCとBoost.Functionを同時に使ってるGiraffe+には致命的な仕様変更です。
この件に起因するバグに遭遇したユーザが問題を把握しやすいよう、基本的なことから解説しておきます。
まず、Boehm GCはマークスウィープ式というやつです。アクセス不可能になったメモリブロックを自動的に開放します。アクセス不可能かどうかの判断は、GCが監視してるメモリブロックから辿れる(traceable)かどうかでなされます。
GCが監視してない領域にあるものは、それがプログラムのほかの部分からアクセスされる予定であっても開放されます。そしてその場合、不正なポインタにアクセスするという典型的で致命的なバグになります。
メモリ領域には、スタックとヒープがあります。自動変数が置かれるのがスタック、動的に確保されるのがヒープです。
スタックはスレッドに付随するメモリ領域ですので、スレッド作成時にGCに新しいスレッドが始まったことを伝えると、そのスタックは監視対象となります。メインスレッドは自動的に監視されます。よって、スタックに関しては、スレッド開始時以外に特に意識することはありません。
一方、ヒープは問題が複雑になります。C言語なら、ヒープを扱うのはmalloc系の関数だけですから、Boehm GCはそれらを置き換えるだけで何の問題もなく動作します。しかし、C++では、newとallocatorが存在します。newに関しては、デストラクタに関してまた別の面倒があるのですが、このBoost.Functionの新仕様と無関係ですので説明を省きます。(ソースのgs/gc/template.hpp参照)
さて、やっとアロケータが出てきました。馴染みの無い人が多いはずですので、何をするか簡単に説明すると、要はSTLのコンテナが呼ぶmallocとfreeです。std::vectorのようなものを適当に作る場合を考えてください。mallocとfreeを使う場面があるはずです。そこがアロケータの仕事場です。
STLコンテナの標準アロケータであるstd::allocatorが確保したメモリブロックはGCの監視対象ではありませんから、アロケータ指定無しのSTLコンテナにGCが管理するメモリブロックのポインタを置いてもGCからは見えません。そしてそのポインタは開放され、ダングリングポインタとなります。
そこで、作成されたメモリブロックが自動的にGCの監視対象になる、そんなアロケータが必要になります。traceable_allocatorがそれです。(ソースのgs/gc/allocator.hpp参照。(Boehm GC付属のものはバグがあってboost::functionでは使えない。)(gc_allocatorはこの件と関係ないので説明略。))
STLコンテナのアロケータ指定にtraceable_allocatorを置くことで、そのコンテナの要素のメモリブロックはGCの監視対象になり、そこに置かれたGCの回収対象であるメモリブロックは開放されません。これでやっとBoehm GCC++のアロケータとの基本的な話は終わりです。
Boost.Functionはファンクタを保持できます。ファンクタはオブジェクトであり、データを保持します。そのデータにGCの回収対象のポインタが置かれる場面があります。それがboost::functionのコンストラクタの第二引数にtraceable_allocatorを渡す場面です。この場面でそうしない場合、先に挙げたSTLコンテナでの問題が同じように発生します。
これまでのBoost.Functionの仕様ではテンプレートによるアロケータの指定でしたから、そうしなければいけない可能性のあるGiraffe+内のboost::functionの全てにtraceable_allocatorを使用していました。今回の仕様変更により、インスタンス毎に個別に指定できるようになりましたから、GCの回収効率向上のため、必要な部分だけにtraceable_allocatorを使用するようにしました。
これにより、新たなバグがGiraffe+に潜んでいる、と私は考えています。使っていて、ソースを見てて、そんな気がする場面があれば指摘してください。ファンクタをメソッド化してる場面はわかりやすいですが、boost::bindやlambdaでGC管理のオブジェクト(主にgs::Object)をバインドして、その戻り値をfunctionに代入してる場合等が怪しいです。


filesystemの変更:
ドキュメントのChange Logに載ってませんが、名前の変更が少しあったようです。木の概念による呼称をやめるみたいです。gs内の対応するメソッドも同じように変更しました。

  • path::has_leafがhas_filenameになった
  • path::has_branch_pathがhas_parent_pathになった
  • path::remove_leafがremove_filenameになった
  • directory_entry::replace_leafがreplace_filenameになった

名前以外でも変更があります。
以前の仕様を忘れましたが、filesystem::removeの仕様が、失敗したら例外を投げるようになり、戻り値がboolからvoidになりました。他の関数と同じく、error_codeを引数に追加することで、例外を投げるのを抑止し、エラーコードを得ることが出来ます。ファイルが存在しない場合は失敗扱いにはなりません。それから、filesystem::remove_allが追加されていたので、Path.remove_allとして定義しました。

値プロパティの無駄なメソッド化を廃止

selfの保持するデータの参照ではなく、enum等、単に値を返すだけのプロパティを、無駄にメソッド化していたのをやめて、そのまま値を保持するようにしました。
そんなに数のあるオブジェクトではない(500も無いはず)ので、メモリ使用量の減少は無意味なほど小さいですが、重要なのは、GCが監視するメモリ領域の縮小が、メモリの開放されやすさとその作業の短時間化に繋がることです。