yohhoyの日記

技術的メモをしていきたい日記

ミューテックスのロック状態を取得

C++11標準ライブラリ提供の ミューテックス および ロックオブジェクト とロック状態の取得について。

std::mutexなどのミューテックス型は、自スレッドでロック獲得済みかを問合せるインタフェースを提供しない。代わりにstd::unique_lockクラステンプレートのowns_lockメンバ関数を用いれば現在のロック状態を確認できる。*1

#include <mutex>
#include <cassert>

std::mutex m1, m2;

{
  std::unique_lock<decltype(m1)> lk1(m1);
  std::unique_lock<decltype(m2)> lk2(m2, std::defer_lock);

  assert(  lk1.owns_lock() );  // ロック獲得済み
  assert( !lk2.owns_lock() );  // 未ロック状態
}

下記コードのように、自スレッドでロック獲得済みの非再帰的なミューテックスstd::mutexstd::timed_mutexに対し、try_lockメンバ関数などを “ロック状態問合せ” を意図して利用すると未定義動作(undefined behavior)を引き起こす。タイムアウト付きロック獲得try_lock_for/untilであっても同様に未定義動作。*2

std::mutex m;
{
  m.lock();
  // mのロック獲得済み

  assert( !m.try_lock() );  // NG: 未定義動作!
  m.unlock();
}

メモ:そもそも直接ミューテックスオブジェクトのlock/unlockを呼び出すのではなく、通常はロックオブジェクト経由でミューテックスのロック獲得/解放操作を行うべき。またロックオブジェクトはScoped Lockイディオムを実現するものであり、ロック状態は “ロックオブジェクト変数のスコープ” として既に表現されていると解釈される。このとき、実行時に確認したロック状態に依存して処理内容を切替えというユースケースは希少(or 設計が不適切)だと思う。実行パスとして複数回ロック獲得操作が起こり得るなら、設計段階で再帰ロック可能なミューテックスを採用すればよい。

関連URL

*1:一般的に、ミューテックスオブジェクト自身が“ロック状態問合せ”インタフェースを提供することは無い。

*2:再帰ロック可能なミューテックス std::recursive_mutex, std::recursive_timed_mutex の場合は、追加のロック獲得に成功するだけであり、やはりロック状態問合せという目的は果たせない。