2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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_
22 #include <tbb/concurrent_hash_map.h>
24 #include "pthread-spin-lock-shim.h"
27 #include "hphp/util/portability.h"
28 #include "hphp/util/assertions.h"
29 #include "hphp/util/rank.h"
32 ///////////////////////////////////////////////////////////////////////////////
34 template <bool enableAssertions
>
38 static const int kMagic
= 0xba5eba11;
41 // m_owner/m_hasOwner for keeping track of lock ownership, useful for debugging
43 unsigned int m_acquires
;
47 inline void recordAcquisition() {
49 if (enableAssertions
) {
51 pthread_equal(m_owner
, pthread_self()));
52 assert(m_acquires
== 0 ||
53 pthread_equal(m_owner
, pthread_self()));
56 m_owner
= pthread_self();
58 assert(m_recursive
|| m_acquires
== 1);
62 inline void invalidateOwner() {
64 if (enableAssertions
) {
70 inline void recordRelease() {
72 if (enableAssertions
) {
75 assert(m_acquires
> 0);
76 if (--m_acquires
== 0) {
83 inline void assertNotOwned() const {
85 if (enableAssertions
) {
87 assert(m_acquires
== 0);
91 inline void assertOwnedBySelf() const {
93 if (enableAssertions
) {
95 assert(pthread_equal(m_owner
, pthread_self()));
96 assert(m_acquires
> 0);
101 explicit BaseMutex(bool recursive
= true, Rank r
= RankUnranked
) {
102 pthread_mutexattr_init(&m_mutexattr
);
104 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_RECURSIVE
);
106 #if defined(__APPLE__)
107 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_DEFAULT
);
109 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_ADAPTIVE_NP
);
112 pthread_mutex_init(&m_mutex
, &m_mutexattr
);
117 m_recursive
= recursive
;
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 BaseMutex(const BaseMutex
&); // suppress
167 BaseMutex
&operator=(const BaseMutex
&); // suppress
170 pthread_mutexattr_t m_mutexattr
;
171 pthread_mutex_t m_mutex
;
175 * Standard recursive mutex, which can be used for condition variables.
176 * This mutex does not support enabling assertions
178 class Mutex
: public BaseMutex
<false> {
180 explicit Mutex(bool recursive
= true, Rank rank
= RankUnranked
) :
181 BaseMutex
<false>(recursive
, rank
) {}
182 pthread_mutex_t
&getRaw() { return m_mutex
; }
186 * Simple recursive mutex, which does not expose the underlying raw
187 * pthread_mutex_t. This allows this mutex to support enabling assertions
189 class SimpleMutex
: public BaseMutex
<true> {
191 explicit SimpleMutex(bool recursive
= true, Rank rank
= RankUnranked
) :
192 BaseMutex
<true>(recursive
, rank
) {}
195 ///////////////////////////////////////////////////////////////////////////////
198 * Read-write lock wrapper.
200 class ReadWriteMutex
{
203 * We have a track record of self-deadlocking on these, and our pthread
204 * implementation tends to do crazy things when a rwlock is double-wlocked,
205 * so check and assert early in debug builds.
207 static constexpr pthread_t InvalidThread
= (pthread_t
)0;
208 pthread_t m_writeOwner
;
212 void invalidateWriteOwner() {
214 m_writeOwner
= InvalidThread
;
218 void recordWriteAcquire() {
220 assert(m_writeOwner
== InvalidThread
);
221 m_writeOwner
= pthread_self();
225 void assertNotWriteOwner() {
227 assert(m_writeOwner
!= pthread_self());
231 void assertNotWriteOwned() {
233 assert(m_writeOwner
== InvalidThread
);
238 explicit ReadWriteMutex(Rank rank
= RankUnranked
)
243 invalidateWriteOwner();
244 pthread_rwlock_init(&m_rwlock
, nullptr);
248 assertNotWriteOwned();
249 pthread_rwlock_destroy(&m_rwlock
);
254 * Atomically downgrading a write lock to a read lock is not part of the
255 * pthreads standard. See task #528421.
257 assertNotWriteOwner();
259 pthread_rwlock_rdlock(&m_rwlock
);
261 * Again, see task #528421.
263 assertNotWriteOwned();
266 void acquireWrite() {
267 assertNotWriteOwner();
269 pthread_rwlock_wrlock(&m_rwlock
);
270 assertNotWriteOwned();
271 recordWriteAcquire();
274 bool attemptRead() { return !pthread_rwlock_tryrdlock(&m_rwlock
); }
275 bool attemptWrite() { return !pthread_rwlock_trywrlock(&m_rwlock
); }
279 if (m_writeOwner
== pthread_self()) {
280 invalidateWriteOwner();
283 pthread_rwlock_unlock(&m_rwlock
);
287 ReadWriteMutex(const ReadWriteMutex
&); // suppress
288 ReadWriteMutex
&operator=(const ReadWriteMutex
&); // suppress
290 pthread_rwlock_t m_rwlock
;
294 * A ranked wrapper around tbb::concurrent_hash_map.
296 template<typename K
, typename V
, typename H
=K
, Rank R
=RankUnranked
>
297 class RankedCHM
: public tbb::concurrent_hash_map
<K
, V
, H
> {
298 typedef tbb::concurrent_hash_map
<K
, V
, H
> RawCHM
;
300 class accessor
: public RawCHM::accessor
{
303 accessor() : freed(false) { pushRank(R
); }
304 ~accessor() { if (!freed
) popRank(R
); }
306 RawCHM::accessor::release();
311 class const_accessor
: public RawCHM::const_accessor
{
314 const_accessor() : freed(false) { pushRank(R
); }
315 ~const_accessor() { if (!freed
) popRank(R
); }
317 RawCHM::const_accessor::release();
323 bool find(const_accessor
& a
, const K
& k
) const { return RawCHM::find(a
, k
); }
324 bool find(accessor
& a
, const K
& k
) { return RawCHM::find(a
, k
); }
325 bool insert(accessor
& a
, const K
& k
) { return RawCHM::insert(a
, k
); }
326 bool insert(const_accessor
& a
, const K
& k
) { return RawCHM::insert(a
, k
); }
327 bool erase(accessor
& a
) { return RawCHM::erase(a
); }
328 bool erase(const_accessor
& a
) { return RawCHM::erase(a
); }
329 bool erase(const K
& k
) { return RawCHM::erase(k
); }
332 ///////////////////////////////////////////////////////////////////////////////
335 #endif // incl_HPHP_MUTEX_H_