mp::syncとMP_UTILIZE

並列イベント駆動I/Oフレームワーク mpio のバージョン0.3.3をリリースしました。

  • mp::wavy::loopクラスに flush() 関数を追加
  • mp::pthread_scoped_{,rd,wr}lockクラスに owns() 関数を追加
  • mp/wavy.hでstdint.hをinclude
  • Downloads

mpioライブラリには、並列性の高いイベントループの実装だけでなく、便利なユーティリティライブラリが含まれています。
今回はその中から、mp::syncMP_UTILIZE を紹介してみたいと思います。

mp::sync

mp::syncは、マルチスレッドプログラムで便利なユーティリティで、mutexをロックしないとTにアクセスできないようにします。
参考:スレッド間で共有する変数のアクセス権制御を C++ コンパイラで強制する方法
次のように使います:

#include <mp/sync.h>
#include <vector>

int main(void)
{
    // mutexで保護されたstd::vectorを作る
    mp::sync< std::vector<int> > sync;

    // リファレンスを取得する(=ロックする)
    mp::sync< std::vector<int> >::ref ref(sync);

    // 使う
    ref->push_back(1);

    // リファレンスを取得しないとアクセスできない
    // sync.push_back(1);  // コンパイルエラー!

    // スコープを外れると自動的にロック解除される

今回のアップデートで、mp::syncの内部にあるmutexへの参照を取り出せるようになりました。
これにより、条件変数を組み合わせられるようになります:

    mp::pthread_cond cond;

    // size()が1以上になるまで待つ
    while(ref->empty()) {
      cond.wait(ref.get_mutex());
    }
}

MP_UTILIZE

MP_UTILIZEは、privateなメンバ関数の宣言をクラス宣言(ヘッダ)の中に書かずに、ソースファイルの中に隠蔽できるようにします。
C++では、プログラムが大きくなってくるとコンパイル時間が死活問題になってきます。数多くのソースから参照されているヘッダファイルを変更するとコンパイルに時間がかかり、やる気がげんなりと下がります。
そこでMP_UTILIZEを使うと、privateな関数を足したり減らしたりするだけなら、ヘッダファイルを書き換えずに済むようになります。


普通は、↓このようにprivateなメンバ関数でもクラス宣言に書かなければなりませんが…

// myclass.h
class myclass {
public:
    void public_func();
private:
    void private_func();
};

MP_UTILIZEを使えば、↓このように隠蔽できます:

// myclass.h
#include <mp/utilize.h>
class myclass {
public:
    void public_func();
private:
    MP_UTILIZE;    // privateな関数を書かなくて済む
};

// myclass.cc
#include "myclass.h"
MP_UTIL_DEF(myclass) {
    void private_func();    // ここに書く
};


隠蔽された関数を呼ぶには、MP_UTILマクロを使います:

void myclass::public_func() {
    MP_UTIL.private_func();
}


MP_UTILIZEの仕掛けは簡単で、MP_UTIL_DEF(CLASS) でCLASSを継承した新しいクラスを定義しており、MP_UTILはthisをそのクラスにstatic_castしてアクセスさせています。
MP_UTILIZEは、そのクラスを宣言した上で、friendクラスに指定しています。


mpioにはこの他にも、mp::sparse_arrayやmp::pthread_signalなど、微妙に便利なユーティリティライブラリが含まれています。
また次の機会には、他のユーティリティも紹介してみたいと思います。