enable coroutine for rust emitter
[hiphop-php.git] / hphp / util / mutex.h
blob0f617383a771154459e850a498bc9fac52672b66
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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_
20 #include <pthread.h>
21 #include <time.h>
22 #include <thread>
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"
30 namespace HPHP {
31 ///////////////////////////////////////////////////////////////////////////////
33 template <bool enableAssertions>
34 struct BaseMutex {
35 private:
36 #ifndef NDEBUG
37 static const int kMagic = 0xba5eba11;
38 int m_magic;
39 Rank m_rank;
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;
43 bool m_recursive;
44 bool m_hasOwner;
45 #endif
46 inline void recordAcquisition() {
47 #ifndef NDEBUG
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());
51 pushRank(m_rank);
52 m_hasOwner = true;
53 m_owner = std::this_thread::get_id();
54 m_acquires++;
55 assert(m_recursive || m_acquires == 1);
57 #endif
59 inline void invalidateOwner() {
60 #ifndef NDEBUG
61 if (enableAssertions) {
62 m_hasOwner = false;
63 m_acquires = 0;
65 #endif
67 inline void recordRelease() {
68 #ifndef NDEBUG
69 if (enableAssertions) {
70 popRank(m_rank);
71 assertOwnedBySelf();
72 assert(m_acquires > 0);
73 if (--m_acquires == 0) {
74 m_hasOwner = false;
77 #endif
79 public:
80 inline void assertNotOwned() const {
81 #ifndef NDEBUG
82 if (enableAssertions) {
83 assert(!m_hasOwner);
84 assert(m_acquires == 0);
86 #endif
88 inline void assertOwnedBySelf() const {
89 #ifndef NDEBUG
90 if (enableAssertions) {
91 assert(m_hasOwner);
92 assert(m_owner == std::this_thread::get_id());
93 assert(m_acquires > 0);
95 #endif
98 public:
99 explicit BaseMutex(bool recursive = true, DEBUG_ONLY Rank r = RankUnranked) {
100 pthread_mutexattr_init(&m_mutexattr);
101 if (recursive) {
102 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_RECURSIVE);
103 } else {
104 #if defined(__APPLE__) || defined(_MSC_VER)
105 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_DEFAULT);
106 #else
107 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_ADAPTIVE_NP);
108 #endif
110 pthread_mutex_init(&m_mutex, &m_mutexattr);
111 #ifndef NDEBUG
112 m_rank = r;
113 m_magic = kMagic;
114 invalidateOwner();
115 m_recursive = recursive;
116 #endif
118 BaseMutex(const BaseMutex&) = delete;
119 BaseMutex& operator=(const BaseMutex&) = delete;
120 ~BaseMutex() {
121 #ifndef NDEBUG
122 assert(m_magic == kMagic);
123 #endif
124 assertNotOwned();
125 pthread_mutex_destroy(&m_mutex);
126 pthread_mutexattr_destroy(&m_mutexattr);
127 #ifndef NDEBUG
128 m_magic = ~m_magic;
129 #endif
132 bool tryLock() {
133 #ifndef NDEBUG
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 #ifndef NDEBUG
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 #ifndef NDEBUG
158 assert(m_magic == kMagic);
159 #endif
160 recordRelease();
161 UNUSED int ret = pthread_mutex_unlock(&m_mutex);
162 assert(ret == 0);
165 protected:
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 {
197 private:
198 #ifndef NDEBUG
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;
205 Rank m_rank;
206 #endif
208 void invalidateWriteOwner() {
209 #ifndef NDEBUG
210 m_writeOwner = std::thread::id();
211 #endif
214 void recordWriteAcquire() {
215 #ifndef NDEBUG
216 assert(m_writeOwner == std::thread::id());
217 m_writeOwner = std::this_thread::get_id();
218 #endif
221 void assertNotWriteOwner() {
222 #ifndef NDEBUG
223 assert(m_writeOwner != std::this_thread::get_id());
224 #endif
227 void assertNotWriteOwned() {
228 #ifndef NDEBUG
229 assert(m_writeOwner == std::thread::id());
230 #endif
233 public:
234 explicit ReadWriteMutex(DEBUG_ONLY Rank rank = RankUnranked)
235 #ifndef NDEBUG
236 : m_rank(rank)
237 #endif
239 invalidateWriteOwner();
240 pthread_rwlock_init(&m_rwlock, nullptr);
243 ReadWriteMutex(const ReadWriteMutex&) = delete;
244 ReadWriteMutex& operator=(const ReadWriteMutex&) = delete;
246 ~ReadWriteMutex() {
247 assertNotWriteOwned();
248 pthread_rwlock_destroy(&m_rwlock);
251 void acquireRead() {
253 * Atomically downgrading a write lock to a read lock is not part of the
254 * pthreads standard. See task #528421.
256 assertNotWriteOwner();
257 pushRank(m_rank);
258 pthread_rwlock_rdlock(&m_rwlock);
260 * Again, see task #528421.
262 assertNotWriteOwned();
265 void acquireWrite() {
266 assertNotWriteOwner();
267 pushRank(m_rank);
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); }
275 void release() {
276 #ifndef NDEBUG
277 popRank(m_rank);
278 if (m_writeOwner == std::this_thread::get_id()) {
279 invalidateWriteOwner();
281 #endif
282 pthread_rwlock_unlock(&m_rwlock);
285 private:
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> {
294 private:
295 typedef tbb::concurrent_hash_map<K, V, H> RawCHM;
296 public:
297 struct accessor : RawCHM::accessor {
298 accessor() : freed(false) { pushRank(R); }
299 ~accessor() { if (!freed) popRank(R); }
300 void release() {
301 RawCHM::accessor::release();
302 popRank(R);
303 freed = true;
305 private:
306 bool freed;
308 struct const_accessor : RawCHM::const_accessor {
309 const_accessor() : freed(false) { pushRank(R); }
310 ~const_accessor() { if (!freed) popRank(R); }
311 void release() {
312 RawCHM::const_accessor::release();
313 popRank(R);
314 freed = true;
316 private:
317 bool freed;
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_