2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_MUTEX_H_
18 #define incl_HPHP_MUTEX_H_
24 #include <tbb/concurrent_hash_map.h>
26 #include "hphp/util/portability.h"
27 #include "hphp/util/assertions.h"
28 #include "hphp/util/rank.h"
31 ///////////////////////////////////////////////////////////////////////////////
33 template <bool enableAssertions
>
37 static const int kMagic
= 0xba5eba11;
40 // m_owner/m_hasOwner for keeping track of lock ownership, useful for debugging
41 std::thread::id m_owner
;
42 unsigned int m_acquires
;
46 inline void recordAcquisition() {
48 if (enableAssertions
) {
49 assert(!m_hasOwner
|| m_owner
== std::this_thread::get_id());
50 assert(m_acquires
== 0 || m_owner
== std::this_thread::get_id());
53 m_owner
= std::this_thread::get_id();
55 assert(m_recursive
|| m_acquires
== 1);
59 inline void invalidateOwner() {
61 if (enableAssertions
) {
67 inline void recordRelease() {
69 if (enableAssertions
) {
72 assert(m_acquires
> 0);
73 if (--m_acquires
== 0) {
80 inline void assertNotOwned() const {
82 if (enableAssertions
) {
84 assert(m_acquires
== 0);
88 inline void assertOwnedBySelf() const {
90 if (enableAssertions
) {
92 assert(m_owner
== std::this_thread::get_id());
93 assert(m_acquires
> 0);
99 explicit BaseMutex(bool recursive
= true, DEBUG_ONLY Rank r
= RankUnranked
) {
100 pthread_mutexattr_init(&m_mutexattr
);
102 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_RECURSIVE
);
104 #if defined(__APPLE__) || defined(_MSC_VER)
105 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_DEFAULT
);
107 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_ADAPTIVE_NP
);
110 pthread_mutex_init(&m_mutex
, &m_mutexattr
);
115 m_recursive
= recursive
;
118 BaseMutex(const BaseMutex
&) = delete;
119 BaseMutex
& operator=(const BaseMutex
&) = delete;
122 assert(m_magic
== kMagic
);
125 pthread_mutex_destroy(&m_mutex
);
126 pthread_mutexattr_destroy(&m_mutexattr
);
134 assert(m_magic
== kMagic
);
136 bool success
= !pthread_mutex_trylock(&m_mutex
);
146 assert(m_magic
== kMagic
);
149 UNUSED
int ret
= pthread_mutex_lock(&m_mutex
);
158 assert(m_magic
== kMagic
);
161 UNUSED
int ret
= pthread_mutex_unlock(&m_mutex
);
166 pthread_mutexattr_t m_mutexattr
;
167 pthread_mutex_t m_mutex
;
171 * Standard recursive mutex, which can be used for condition variables.
172 * This mutex does not support enabling assertions
174 struct Mutex
: BaseMutex
<false> {
175 explicit Mutex(bool recursive
= true, Rank rank
= RankUnranked
) :
176 BaseMutex
<false>(recursive
, rank
) {}
177 explicit Mutex(Rank rank
, bool recursive
= true) :
178 BaseMutex
<false>(recursive
, rank
) {}
179 pthread_mutex_t
&getRaw() { return m_mutex
; }
183 * Simple recursive mutex, which does not expose the underlying raw
184 * pthread_mutex_t. This allows this mutex to support enabling assertions
186 struct SimpleMutex
: BaseMutex
<true> {
187 explicit SimpleMutex(bool recursive
= true, Rank rank
= RankUnranked
) :
188 BaseMutex
<true>(recursive
, rank
) {}
191 ///////////////////////////////////////////////////////////////////////////////
194 * Read-write lock wrapper.
196 struct ReadWriteMutex
{
200 * We have a track record of self-deadlocking on these, and our pthread
201 * implementation tends to do crazy things when a rwlock is double-wlocked,
202 * so check and assert early in debug builds.
204 std::thread::id m_writeOwner
;
208 void invalidateWriteOwner() {
210 m_writeOwner
= std::thread::id();
214 void recordWriteAcquire() {
216 assert(m_writeOwner
== std::thread::id());
217 m_writeOwner
= std::this_thread::get_id();
221 void assertNotWriteOwner() {
223 assert(m_writeOwner
!= std::this_thread::get_id());
227 void assertNotWriteOwned() {
229 assert(m_writeOwner
== std::thread::id());
234 explicit ReadWriteMutex(DEBUG_ONLY Rank rank
= RankUnranked
)
239 invalidateWriteOwner();
240 pthread_rwlock_init(&m_rwlock
, nullptr);
243 ReadWriteMutex(const ReadWriteMutex
&) = delete;
244 ReadWriteMutex
& operator=(const ReadWriteMutex
&) = delete;
247 assertNotWriteOwned();
248 pthread_rwlock_destroy(&m_rwlock
);
253 * Atomically downgrading a write lock to a read lock is not part of the
254 * pthreads standard. See task #528421.
256 assertNotWriteOwner();
258 pthread_rwlock_rdlock(&m_rwlock
);
260 * Again, see task #528421.
262 assertNotWriteOwned();
265 void acquireWrite() {
266 assertNotWriteOwner();
268 pthread_rwlock_wrlock(&m_rwlock
);
269 assertNotWriteOwned();
270 recordWriteAcquire();
273 bool attemptRead() { return !pthread_rwlock_tryrdlock(&m_rwlock
); }
274 bool attemptWrite() { return !pthread_rwlock_trywrlock(&m_rwlock
); }
278 if (m_writeOwner
== std::this_thread::get_id()) {
279 invalidateWriteOwner();
282 pthread_rwlock_unlock(&m_rwlock
);
286 pthread_rwlock_t m_rwlock
;
290 * A ranked wrapper around tbb::concurrent_hash_map.
292 template<typename K
, typename V
, typename H
=K
, Rank R
=RankUnranked
>
293 struct RankedCHM
: tbb::concurrent_hash_map
<K
, V
, H
> {
295 typedef tbb::concurrent_hash_map
<K
, V
, H
> RawCHM
;
297 struct accessor
: RawCHM::accessor
{
298 accessor() : freed(false) { pushRank(R
); }
299 ~accessor() { if (!freed
) popRank(R
); }
301 RawCHM::accessor::release();
308 struct const_accessor
: RawCHM::const_accessor
{
309 const_accessor() : freed(false) { pushRank(R
); }
310 ~const_accessor() { if (!freed
) popRank(R
); }
312 RawCHM::const_accessor::release();
320 bool find(const_accessor
& a
, const K
& k
) const { return RawCHM::find(a
, k
); }
321 bool find(accessor
& a
, const K
& k
) { return RawCHM::find(a
, k
); }
322 bool insert(accessor
& a
, const K
& k
) { return RawCHM::insert(a
, k
); }
323 bool insert(const_accessor
& a
, const K
& k
) { return RawCHM::insert(a
, k
); }
324 bool erase(accessor
& a
) { return RawCHM::erase(a
); }
325 bool erase(const_accessor
& a
) { return RawCHM::erase(a
); }
326 bool erase(const K
& k
) { return RawCHM::erase(k
); }
329 ///////////////////////////////////////////////////////////////////////////////
332 #endif // incl_HPHP_MUTEX_H_