FILENAME 3/4 - delete the old get_fun_path etc.
[hiphop-php.git] / hphp / util / mutex.h
blob169e02ad290a48e76f0d32a2588f9577b79120ca
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 #pragma once
19 #include <pthread.h>
20 #include <time.h>
21 #include <thread>
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"
29 namespace HPHP {
30 ///////////////////////////////////////////////////////////////////////////////
32 template <bool enableAssertions>
33 struct BaseMutex {
34 private:
35 #ifndef NDEBUG
36 static const int kMagic = 0xba5eba11;
37 int m_magic;
38 Rank m_rank;
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;
42 bool m_recursive;
43 bool m_hasOwner;
44 #endif
45 inline void recordAcquisition() {
46 #ifndef NDEBUG
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());
50 pushRank(m_rank);
51 m_hasOwner = true;
52 m_owner = std::this_thread::get_id();
53 m_acquires++;
54 assert(m_recursive || m_acquires == 1);
56 #endif
58 inline void invalidateOwner() {
59 #ifndef NDEBUG
60 if (enableAssertions) {
61 m_hasOwner = false;
62 m_acquires = 0;
64 #endif
66 inline void recordRelease() {
67 #ifndef NDEBUG
68 if (enableAssertions) {
69 popRank(m_rank);
70 assertOwnedBySelf();
71 assert(m_acquires > 0);
72 if (--m_acquires == 0) {
73 m_hasOwner = false;
76 #endif
78 public:
79 inline void assertNotOwned() const {
80 #ifndef NDEBUG
81 if (enableAssertions) {
82 assert(!m_hasOwner);
83 assert(m_acquires == 0);
85 #endif
87 inline void assertOwnedBySelf() const {
88 #ifndef NDEBUG
89 if (enableAssertions) {
90 assert(m_hasOwner);
91 assert(m_owner == std::this_thread::get_id());
92 assert(m_acquires > 0);
94 #endif
97 public:
98 explicit BaseMutex(bool recursive = true, DEBUG_ONLY Rank r = RankUnranked) {
99 pthread_mutexattr_init(&m_mutexattr);
100 if (recursive) {
101 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_RECURSIVE);
102 } else {
103 #if defined(__APPLE__) || defined(_MSC_VER)
104 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_DEFAULT);
105 #else
106 pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_ADAPTIVE_NP);
107 #endif
109 pthread_mutex_init(&m_mutex, &m_mutexattr);
110 #ifndef NDEBUG
111 m_rank = r;
112 m_magic = kMagic;
113 invalidateOwner();
114 m_recursive = recursive;
115 #endif
117 BaseMutex(const BaseMutex&) = delete;
118 BaseMutex& operator=(const BaseMutex&) = delete;
119 ~BaseMutex() {
120 #ifndef NDEBUG
121 assert(m_magic == kMagic);
122 #endif
123 assertNotOwned();
124 pthread_mutex_destroy(&m_mutex);
125 pthread_mutexattr_destroy(&m_mutexattr);
126 #ifndef NDEBUG
127 m_magic = ~m_magic;
128 #endif
131 bool tryLock() {
132 #ifndef NDEBUG
133 assert(m_magic == kMagic);
134 #endif
135 bool success = !pthread_mutex_trylock(&m_mutex);
136 if (success) {
137 recordAcquisition();
138 assertOwnedBySelf();
140 return success;
143 void lock() {
144 #ifndef NDEBUG
145 assert(m_magic == kMagic);
146 checkRank(m_rank);
147 #endif
148 UNUSED int ret = pthread_mutex_lock(&m_mutex);
149 assert(ret == 0);
151 recordAcquisition();
152 assertOwnedBySelf();
155 void unlock() {
156 #ifndef NDEBUG
157 assert(m_magic == kMagic);
158 #endif
159 recordRelease();
160 UNUSED int ret = pthread_mutex_unlock(&m_mutex);
161 assert(ret == 0);
164 protected:
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 {
196 private:
197 #ifndef NDEBUG
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;
204 Rank m_rank;
205 #endif
207 void invalidateWriteOwner() {
208 #ifndef NDEBUG
209 m_writeOwner = std::thread::id();
210 #endif
213 void recordWriteAcquire() {
214 #ifndef NDEBUG
215 assert(m_writeOwner == std::thread::id());
216 m_writeOwner = std::this_thread::get_id();
217 #endif
220 void assertNotWriteOwner() {
221 #ifndef NDEBUG
222 assert(m_writeOwner != std::this_thread::get_id());
223 #endif
226 void assertNotWriteOwned() {
227 #ifndef NDEBUG
228 assert(m_writeOwner == std::thread::id());
229 #endif
232 public:
233 explicit ReadWriteMutex(DEBUG_ONLY Rank rank = RankUnranked)
234 #ifndef NDEBUG
235 : m_rank(rank)
236 #endif
238 invalidateWriteOwner();
239 pthread_rwlock_init(&m_rwlock, nullptr);
242 ReadWriteMutex(const ReadWriteMutex&) = delete;
243 ReadWriteMutex& operator=(const ReadWriteMutex&) = delete;
245 ~ReadWriteMutex() {
246 assertNotWriteOwned();
247 pthread_rwlock_destroy(&m_rwlock);
250 void acquireRead() {
252 * Atomically downgrading a write lock to a read lock is not part of the
253 * pthreads standard. See task #528421.
255 assertNotWriteOwner();
256 pushRank(m_rank);
257 pthread_rwlock_rdlock(&m_rwlock);
259 * Again, see task #528421.
261 assertNotWriteOwned();
264 void acquireWrite() {
265 assertNotWriteOwner();
266 pushRank(m_rank);
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); }
274 void release() {
275 #ifndef NDEBUG
276 popRank(m_rank);
277 if (m_writeOwner == std::this_thread::get_id()) {
278 invalidateWriteOwner();
280 #endif
281 pthread_rwlock_unlock(&m_rwlock);
284 private:
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> {
293 private:
294 typedef tbb::concurrent_hash_map<K, V, H> RawCHM;
295 public:
296 struct accessor : RawCHM::accessor {
297 accessor() : freed(false) { pushRank(R); }
298 ~accessor() { if (!freed) popRank(R); }
299 void release() {
300 RawCHM::accessor::release();
301 popRank(R);
302 freed = true;
304 private:
305 bool freed;
307 struct const_accessor : RawCHM::const_accessor {
308 const_accessor() : freed(false) { pushRank(R); }
309 ~const_accessor() { if (!freed) popRank(R); }
310 void release() {
311 RawCHM::const_accessor::release();
312 popRank(R);
313 freed = true;
315 private:
316 bool freed;
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 ///////////////////////////////////////////////////////////////////////////////