C++11 std::unique_lock の用例「ロック付きスマートポインタ」

Mutexロックの最大の弱点は合成不可能なこと.(Java用語だが)あるクラスにsynchronizedメソッドが2つあったとき,それを順々に呼び出すような関数は一般にスレッドセーフでない.複数のメソッドに渡るクリティカルセクションについて考えるのはときどきめんどいことがある.
そこでスマートポインタ(または一般に資源へのハンドル)をmutexのRAII的ラッパとくっつけてしまうことを考えた.そのスマポがローカル変数としてアクセスできる間は資源は排他的に利用可能.実際,関数の返り値の型をスマートポインタとすることによって,「このオブジェクトのメモリは中で新たにnewされたものだから,後でdeleteせにゃいかんよ」というメッセージを型に込める慣例は古くから(一部で)行われていた.これはそれをmutexロックに敷衍しようという試み.

class UniqResource {
private:
...
    std::mutex m_mtx;
    typedef std::unique_lock<std::mutex> Lock;

public:
...
    class Handle {
    private:
        UniqResource *m_p;
        Lock m_lk;

    public:
        Handle(UniqResource *p, Lock lk)
            : m_p{ p }, m_lk{ std::move(lk) }
        { }

        UniqResource &operator*() { return *m_p; }
        UniqResource *operator->() { return m_p; }
    };

    Handle getLock() {
        return Handle{ this, Lock(m_mtx) };
    }
...
};

使い方はこんな感じ:

{ // the critical section begins here
    auto lk = uRsc.getLock()
    lk->methodA();
    lk->methodB();
    lk->methodC();
} // the critical section ends here

正確に言えば「ロック付きスマポ」そのものはムーブの無いC++98でも実現できると思うが,抜け道がより広く,かつださい.よほど苦労しないと*1ムーブできないので資源を使う側で明示的にその「ロック付きスマポ」をローカル変数として取って,それをロックなし資源へのポインタで初期化するしかないはず.ここでロックなし資源へのポインタが常に見えてしまうのがださい.
なお原理原則で言えば,一般には*2mutexロックはユーザ側が自分のコードを全て理解した俯瞰視点で賢く清く正しく取得しないと常にデッドロックが怖い.だからmutexロックに対する万能薬はたとえ22世紀になっても決して現れない定めであり,上記アイディアも実際悪手にもなり得るが... それでもそこそこ広いクラスの用途に有効になるんじゃないかな.なんとなく私の予想では(複数のmutexの相互作用で起きる)デッドロックよりも,頭すっからかんなロック忘れか,無批判に安全側に倒して無闇に広いクリティカルセクションとかの方が世に蔓延していると思う.この「ロック付きスマポ」はそれらをカジュアルに撲滅するのに役立つと期待している.
という訳でC++11のムーブ便利! という話でした.(しかし未だに右辺値参照が何から/何へキャスト可能か頭に入ってない... orz)

*1:自分で参照カウントのロジックを書くかproxyを書く http://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/%E6%89%80%E6%9C%89%E6%A8%A9%E7%A7%BB%E5%8B%95%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%28Move_Constructor%29

*2:この「一般」は日常的な用例の「よくある場合では」という意味でなく科学技術用語のそれで,つまり「mutexロックの使われ方についてあらゆるものを想定から排除しない/できないとき」ということ.