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 +----------------------------------------------------------------------+
23 #include <tbb/concurrent_hash_map.h>
25 #include "hphp/util/portability.h"
26 #include "hphp/util/assertions.h"
27 #include "hphp/util/rank.h"
30 ///////////////////////////////////////////////////////////////////////////////
32 template <bool enableAssertions
>
36 static const int kMagic
= 0xba5eba11;
39 // m_owner/m_hasOwner for keeping track of lock ownership, useful for debugging
40 std::thread::id m_owner
;
41 unsigned int m_acquires
;
45 inline void recordAcquisition() {
47 if (enableAssertions
) {
48 assert(!m_hasOwner
|| m_owner
== std::this_thread::get_id());
49 assert(m_acquires
== 0 || m_owner
== std::this_thread::get_id());
52 m_owner
= std::this_thread::get_id();
54 assert(m_recursive
|| m_acquires
== 1);
58 inline void invalidateOwner() {
60 if (enableAssertions
) {
66 inline void recordRelease() {
68 if (enableAssertions
) {
71 assert(m_acquires
> 0);
72 if (--m_acquires
== 0) {
79 inline void assertNotOwned() const {
81 if (enableAssertions
) {
83 assert(m_acquires
== 0);
87 inline void assertOwnedBySelf() const {
89 if (enableAssertions
) {
91 assert(m_owner
== std::this_thread::get_id());
92 assert(m_acquires
> 0);
98 explicit BaseMutex(bool recursive
= true, DEBUG_ONLY Rank r
= RankUnranked
) {
99 pthread_mutexattr_init(&m_mutexattr
);
101 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_RECURSIVE
);
103 #if defined(__APPLE__) || defined(_MSC_VER)
104 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_DEFAULT
);
106 pthread_mutexattr_settype(&m_mutexattr
, PTHREAD_MUTEX_ADAPTIVE_NP
);
109 pthread_mutex_init(&m_mutex
, &m_mutexattr
);
114 m_recursive
= recursive
;
117 BaseMutex(const BaseMutex
&) = delete;
118 BaseMutex
& operator=(const BaseMutex
&) = delete;
121 assert(m_magic
== kMagic
);
124 pthread_mutex_destroy(&m_mutex
);
125 pthread_mutexattr_destroy(&m_mutexattr
);
133 assert(m_magic
== kMagic
);
135 bool success
= !pthread_mutex_trylock(&m_mutex
);
145 assert(m_magic
== kMagic
);
148 UNUSED
int ret
= pthread_mutex_lock(&m_mutex
);
157 assert(m_magic
== kMagic
);
160 UNUSED
int ret
= pthread_mutex_unlock(&m_mutex
);
165 pthread_mutexattr_t m_mutexattr
;
166 pthread_mutex_t m_mutex
;
170 * Standard recursive mutex, which can be used for condition variables.
171 * This mutex does not support enabling assertions
173 struct Mutex
: BaseMutex
<false> {
174 explicit Mutex(bool recursive
= true, Rank rank
= RankUnranked
) :
175 BaseMutex
<false>(recursive
, rank
) {}
176 explicit Mutex(Rank rank
, bool recursive
= true) :
177 BaseMutex
<false>(recursive
, rank
) {}
178 pthread_mutex_t
&getRaw() { return m_mutex
; }
182 * Simple recursive mutex, which does not expose the underlying raw
183 * pthread_mutex_t. This allows this mutex to support enabling assertions
185 struct SimpleMutex
: BaseMutex
<true> {
186 explicit SimpleMutex(bool recursive
= true, Rank rank
= RankUnranked
) :
187 BaseMutex
<true>(recursive
, rank
) {}
190 ///////////////////////////////////////////////////////////////////////////////
193 * Read-write lock wrapper.
195 struct ReadWriteMutex
{
199 * We have a track record of self-deadlocking on these, and our pthread
200 * implementation tends to do crazy things when a rwlock is double-wlocked,
201 * so check and assert early in debug builds.
203 std::thread::id m_writeOwner
;
207 void invalidateWriteOwner() {
209 m_writeOwner
= std::thread::id();
213 void recordWriteAcquire() {
215 assert(m_writeOwner
== std::thread::id());
216 m_writeOwner
= std::this_thread::get_id();
220 void assertNotWriteOwner() {
222 assert(m_writeOwner
!= std::this_thread::get_id());
226 void assertNotWriteOwned() {
228 assert(m_writeOwner
== std::thread::id());
233 explicit ReadWriteMutex(DEBUG_ONLY Rank rank
= RankUnranked
)
238 invalidateWriteOwner();
239 pthread_rwlock_init(&m_rwlock
, nullptr);
242 ReadWriteMutex(const ReadWriteMutex
&) = delete;
243 ReadWriteMutex
& operator=(const ReadWriteMutex
&) = delete;
246 assertNotWriteOwned();
247 pthread_rwlock_destroy(&m_rwlock
);
252 * Atomically downgrading a write lock to a read lock is not part of the
253 * pthreads standard. See task #528421.
255 assertNotWriteOwner();
257 pthread_rwlock_rdlock(&m_rwlock
);
259 * Again, see task #528421.
261 assertNotWriteOwned();
264 void acquireWrite() {
265 assertNotWriteOwner();
267 pthread_rwlock_wrlock(&m_rwlock
);
268 assertNotWriteOwned();
269 recordWriteAcquire();
272 bool attemptRead() { return !pthread_rwlock_tryrdlock(&m_rwlock
); }
273 bool attemptWrite() { return !pthread_rwlock_trywrlock(&m_rwlock
); }
277 if (m_writeOwner
== std::this_thread::get_id()) {
278 invalidateWriteOwner();
281 pthread_rwlock_unlock(&m_rwlock
);
285 pthread_rwlock_t m_rwlock
;
289 * A ranked wrapper around tbb::concurrent_hash_map.
291 template<typename K
, typename V
, typename H
=K
, Rank R
=RankUnranked
>
292 struct RankedCHM
: tbb::concurrent_hash_map
<K
, V
, H
> {
294 typedef tbb::concurrent_hash_map
<K
, V
, H
> RawCHM
;
296 struct accessor
: RawCHM::accessor
{
297 accessor() : freed(false) { pushRank(R
); }
298 ~accessor() { if (!freed
) popRank(R
); }
300 RawCHM::accessor::release();
307 struct const_accessor
: RawCHM::const_accessor
{
308 const_accessor() : freed(false) { pushRank(R
); }
309 ~const_accessor() { if (!freed
) popRank(R
); }
311 RawCHM::const_accessor::release();
319 bool find(const_accessor
& a
, const K
& k
) const { return RawCHM::find(a
, k
); }
320 bool find(accessor
& a
, const K
& k
) { return RawCHM::find(a
, k
); }
321 bool insert(accessor
& a
, const K
& k
) { return RawCHM::insert(a
, k
); }
322 bool insert(const_accessor
& a
, const K
& k
) { return RawCHM::insert(a
, k
); }
323 bool erase(accessor
& a
) { return RawCHM::erase(a
); }
324 bool erase(const_accessor
& a
) { return RawCHM::erase(a
); }
325 bool erase(const K
& k
) { return RawCHM::erase(k
); }
328 ///////////////////////////////////////////////////////////////////////////////