C++標準ライブラリが提供するミューテックス*1に対するロック試行操作try_lockメンバ関数は、ロック獲得可能なときでも失敗する可能性がある(spurious failure)*2


16 Effects: Attempts to obtain ownership of the mutex for the calling thread without blocking. If ownership is not obtained, there is no effect and try_lock() immediately returns. An implementation may fail to obtain the lock even if it is not held by any other thread. [Note: This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange (Clause 29). -- end note] An implementation should ensure that try_lock() does not consistently return false in the absence of contending mutex acquisitions.

このようなtry_lockの spurious failure は一般的(POSIXpthread_mutex_trylock等)には見られない仕様。Java言語のようにデータ競合(data race)にまつわる複雑なメモリモデルの導入を避けるために追加されたらしい(データ競合とメモリモデルについては、id:yohhoy:20120405も参考に)

"Foundations of the C++ Concurrency Memory Model" より関連箇所を部分引用。

We use weaker semantics for trylock than existing languages or libraries, allowing us to promise sequential consistency with an intuitive race definition, even for programs with trylock.

3. Making Trylock Efficient
Instead of introducing the complexity of different types of synchronization or a happens-before relationship to define a data race, we propose a simple solution. We change the specification of trylock(), though preferably not its implementation, to not guarantee that it will succeed if the lock is available. The C++0x specification is expected to allow trylock to "spuriously fail" in this manner.

メモ:[現状理解を記載。正確性は保証しない。]ある種のコード動作をサポートするためにlock処理とtry_lock失敗処理を同期させるには、lock操作にreleaseセマンテイクスも持たせる必要がある。ただしこれはミューテックス通常利用における unlock/lock 間の同期では不要であり、またロック競合(contention)がなくとも高コストなメモリバリアが必要となる。ゆえに、C++11標準ではtry_lock失敗処理が一切のメモリ操作セマンティクスを持たないと決めた。つまりtry_lockが存在したとしても spurious failure を許容し、常にロック状態をreadするものではないと定義する。


*1:std::mutex, std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex

*2:あくまでも “誤って(spurious)失敗することが起きえる” という意味。引用の後半にあるように、ロック獲得可能な状況下で一貫して失敗すること(例えば常にfalseを返す実装)は許容されない。