make (array) of ArrayObject return the contents
[hiphop-php.git] / hphp / util / mutex.h
blobfcc774585eaf4bf5cadaf21bd33e8c11747b46ce
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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_
20 #include "hphp/util/assertions.h"
21 #include "hphp/util/util.h"
22 #include <pthread.h>
23 #include <time.h>
24 #include <tbb/concurrent_hash_map.h>
25 #ifdef __APPLE__
26 #include "pthread-spin-lock-shim.h"
27 #endif
29 #include "hphp/util/rank.h"
31 namespace HPHP {
32 ///////////////////////////////////////////////////////////////////////////////
34 template <bool enableAssertions>
35 class BaseMutex {
36 private:
37 #ifdef DEBUG
38 static const int kMagic = 0xba5eba11;
39 int m_magic;
40 Rank m_rank;
41 // m_owner/m_hasOwner for keeping track of lock ownership, useful for debugging
42 pthread_t m_owner;
43 unsigned int m_acquires;
44 bool m_recursive;
45 bool m_hasOwner;
46 #endif
47 inline void recordAcquisition() {
48 #ifdef DEBUG
49 if (enableAssertions) {
50 assert(!m_hasOwner ||
51 pthread_equal(m_owner, pthread_self()));
52 assert(m_acquires == 0 ||
53 pthread_equal(m_owner, pthread_self()));
54 pushRank(m_rank);
55 m_hasOwner = true;
56 m_owner = pthread_self();
57 m_acquires++;
58 assert(m_recursive || m_acquires == 1);
60 #endif
62 inline void invalidateOwner() {
63 #ifdef DEBUG
64 if (enableAssertions) {
65 m_hasOwner = false;
66 m_acquires = 0;
68 #endif
70 inline void recordRelease() {
71 #ifdef DEBUG
72 if (enableAssertions) {
73 popRank(m_rank);
74 assertOwnedBySelf();
75 assert(m_acquires > 0);
76 if (--m_acquires == 0) {
77 m_hasOwner = false;
80 #endif
82 public:
83 inline void assertNotOwned() const {
84 #ifdef DEBUG
85 if (enableAssertions) {
86 assert(!m_hasOwner);
87 assert(m_acquires == 0);
89 #endif
91 inline void assertOwnedBySelf() const {
92 #ifdef DEBUG
93 if (enableAssertions) {
94 assert(m_hasOwner);
95 assert(pthread_equal(m_owner, pthread_self()));
96 assert(m_acquires > 0);
98 #endif
100 public:
101 explicit BaseMutex(bool recursive = true, Rank r = RankUnranked) {
102 pthread_mutexattr_init(&m_mutexattr);
103 if (recursive) {
104 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_RECURSIVE);
105 } else {
106 #if defined(__APPLE__)
107 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_DEFAULT);
108 #else
109 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_ADAPTIVE_NP);
110 #endif
112 pthread_mutex_init(&m_mutex, &m_mutexattr);
113 #ifdef DEBUG
114 m_rank = r;
115 m_magic = kMagic;
116 invalidateOwner();
117 m_recursive = recursive;
118 #endif
120 ~BaseMutex() {
121 #ifdef DEBUG
122 assert(m_magic == kMagic);
123 #endif
124 assertNotOwned();
125 pthread_mutex_destroy(&m_mutex);
126 pthread_mutexattr_destroy(&m_mutexattr);
127 #ifdef DEBUG
128 m_magic = ~m_magic;
129 #endif
132 bool tryLock() {
133 #ifdef DEBUG
134 assert(m_magic == kMagic);
135 #endif
136 bool success = !pthread_mutex_trylock(&m_mutex);
137 if (success) {
138 recordAcquisition();
139 assertOwnedBySelf();
141 return success;
144 void lock() {
145 #ifdef DEBUG
146 assert(m_magic == kMagic);
147 checkRank(m_rank);
148 #endif
149 UNUSED int ret = pthread_mutex_lock(&m_mutex);
150 assert(ret == 0);
152 recordAcquisition();
153 assertOwnedBySelf();
156 void unlock() {
157 #ifdef DEBUG
158 assert(m_magic == kMagic);
159 #endif
160 recordRelease();
161 UNUSED int ret = pthread_mutex_unlock(&m_mutex);
162 assert(ret == 0);
165 private:
166 BaseMutex(const BaseMutex &); // suppress
167 BaseMutex &operator=(const BaseMutex &); // suppress
169 protected:
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> {
179 public:
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> {
190 public:
191 explicit SimpleMutex(bool recursive = true, Rank rank = RankUnranked) :
192 BaseMutex<true>(recursive, rank) {}
195 ///////////////////////////////////////////////////////////////////////////////
198 * Read-write lock wrapper.
200 class ReadWriteMutex {
201 #ifdef DEBUG
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;
209 Rank m_rank;
210 #endif
212 void invalidateWriteOwner() {
213 #ifdef DEBUG
214 m_writeOwner = InvalidThread;
215 #endif
218 void recordWriteAcquire() {
219 #ifdef DEBUG
220 assert(m_writeOwner == InvalidThread);
221 m_writeOwner = pthread_self();
222 #endif
225 void assertNotWriteOwner() {
226 #ifdef DEBUG
227 assert(m_writeOwner != pthread_self());
228 #endif
231 void assertNotWriteOwned() {
232 #ifdef DEBUG
233 assert(m_writeOwner == InvalidThread);
234 #endif
237 public:
238 explicit ReadWriteMutex(Rank rank = RankUnranked)
239 #ifdef DEBUG
240 : m_rank(rank)
241 #endif
243 invalidateWriteOwner();
244 pthread_rwlock_init(&m_rwlock, nullptr);
247 ~ReadWriteMutex() {
248 assertNotWriteOwned();
249 pthread_rwlock_destroy(&m_rwlock);
252 void acquireRead() {
254 * Atomically downgrading a write lock to a read lock is not part of the
255 * pthreads standard. See task #528421.
257 assertNotWriteOwner();
258 pushRank(m_rank);
259 pthread_rwlock_rdlock(&m_rwlock);
261 * Again, see task #528421.
263 assertNotWriteOwned();
266 void acquireWrite() {
267 assertNotWriteOwner();
268 pushRank(m_rank);
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); }
276 void release() {
277 #ifdef DEBUG
278 popRank(m_rank);
279 if (m_writeOwner == pthread_self()) {
280 invalidateWriteOwner();
282 #endif
283 pthread_rwlock_unlock(&m_rwlock);
286 private:
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;
299 public:
300 class accessor : public RawCHM::accessor {
301 bool freed;
302 public:
303 accessor() : freed(false) { pushRank(R); }
304 ~accessor() { if (!freed) popRank(R); }
305 void release() {
306 RawCHM::accessor::release();
307 popRank(R);
308 freed = true;
311 class const_accessor : public RawCHM::const_accessor {
312 bool freed;
313 public:
314 const_accessor() : freed(false) { pushRank(R); }
315 ~const_accessor() { if (!freed) popRank(R); }
316 void release() {
317 RawCHM::const_accessor::release();
318 popRank(R);
319 freed = true;
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_