cannam@147
|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
cannam@147
|
2 // Licensed under the MIT License:
|
cannam@147
|
3 //
|
cannam@147
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
cannam@147
|
5 // of this software and associated documentation files (the "Software"), to deal
|
cannam@147
|
6 // in the Software without restriction, including without limitation the rights
|
cannam@147
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
cannam@147
|
8 // copies of the Software, and to permit persons to whom the Software is
|
cannam@147
|
9 // furnished to do so, subject to the following conditions:
|
cannam@147
|
10 //
|
cannam@147
|
11 // The above copyright notice and this permission notice shall be included in
|
cannam@147
|
12 // all copies or substantial portions of the Software.
|
cannam@147
|
13 //
|
cannam@147
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
cannam@147
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
cannam@147
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
cannam@147
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
cannam@147
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
cannam@147
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
cannam@147
|
20 // THE SOFTWARE.
|
cannam@147
|
21
|
cannam@147
|
22 #ifndef KJ_MUTEX_H_
|
cannam@147
|
23 #define KJ_MUTEX_H_
|
cannam@147
|
24
|
cannam@147
|
25 #if defined(__GNUC__) && !KJ_HEADER_WARNINGS
|
cannam@147
|
26 #pragma GCC system_header
|
cannam@147
|
27 #endif
|
cannam@147
|
28
|
cannam@147
|
29 #include "memory.h"
|
cannam@147
|
30 #include <inttypes.h>
|
cannam@147
|
31
|
cannam@147
|
32 #if __linux__ && !defined(KJ_USE_FUTEX)
|
cannam@147
|
33 #define KJ_USE_FUTEX 1
|
cannam@147
|
34 #endif
|
cannam@147
|
35
|
cannam@147
|
36 #if !KJ_USE_FUTEX && !_WIN32
|
cannam@147
|
37 // On Linux we use futex. On other platforms we wrap pthreads.
|
cannam@147
|
38 // TODO(someday): Write efficient low-level locking primitives for other platforms.
|
cannam@147
|
39 #include <pthread.h>
|
cannam@147
|
40 #endif
|
cannam@147
|
41
|
cannam@147
|
42 namespace kj {
|
cannam@147
|
43
|
cannam@147
|
44 // =======================================================================================
|
cannam@147
|
45 // Private details -- public interfaces follow below.
|
cannam@147
|
46
|
cannam@147
|
47 namespace _ { // private
|
cannam@147
|
48
|
cannam@147
|
49 class Mutex {
|
cannam@147
|
50 // Internal implementation details. See `MutexGuarded<T>`.
|
cannam@147
|
51
|
cannam@147
|
52 public:
|
cannam@147
|
53 Mutex();
|
cannam@147
|
54 ~Mutex();
|
cannam@147
|
55 KJ_DISALLOW_COPY(Mutex);
|
cannam@147
|
56
|
cannam@147
|
57 enum Exclusivity {
|
cannam@147
|
58 EXCLUSIVE,
|
cannam@147
|
59 SHARED
|
cannam@147
|
60 };
|
cannam@147
|
61
|
cannam@147
|
62 void lock(Exclusivity exclusivity);
|
cannam@147
|
63 void unlock(Exclusivity exclusivity);
|
cannam@147
|
64
|
cannam@147
|
65 void assertLockedByCaller(Exclusivity exclusivity);
|
cannam@147
|
66 // In debug mode, assert that the mutex is locked by the calling thread, or if that is
|
cannam@147
|
67 // non-trivial, assert that the mutex is locked (which should be good enough to catch problems
|
cannam@147
|
68 // in unit tests). In non-debug builds, do nothing.
|
cannam@147
|
69
|
cannam@147
|
70 private:
|
cannam@147
|
71 #if KJ_USE_FUTEX
|
cannam@147
|
72 uint futex;
|
cannam@147
|
73 // bit 31 (msb) = set if exclusive lock held
|
cannam@147
|
74 // bit 30 (msb) = set if threads are waiting for exclusive lock
|
cannam@147
|
75 // bits 0-29 = count of readers; If an exclusive lock is held, this is the count of threads
|
cannam@147
|
76 // waiting for a read lock, otherwise it is the count of threads that currently hold a read
|
cannam@147
|
77 // lock.
|
cannam@147
|
78
|
cannam@147
|
79 static constexpr uint EXCLUSIVE_HELD = 1u << 31;
|
cannam@147
|
80 static constexpr uint EXCLUSIVE_REQUESTED = 1u << 30;
|
cannam@147
|
81 static constexpr uint SHARED_COUNT_MASK = EXCLUSIVE_REQUESTED - 1;
|
cannam@147
|
82
|
cannam@147
|
83 #elif _WIN32
|
cannam@147
|
84 uintptr_t srwLock; // Actually an SRWLOCK, but don't want to #include <windows.h> in header.
|
cannam@147
|
85
|
cannam@147
|
86 #else
|
cannam@147
|
87 mutable pthread_rwlock_t mutex;
|
cannam@147
|
88 #endif
|
cannam@147
|
89 };
|
cannam@147
|
90
|
cannam@147
|
91 class Once {
|
cannam@147
|
92 // Internal implementation details. See `Lazy<T>`.
|
cannam@147
|
93
|
cannam@147
|
94 public:
|
cannam@147
|
95 #if KJ_USE_FUTEX
|
cannam@147
|
96 inline Once(bool startInitialized = false)
|
cannam@147
|
97 : futex(startInitialized ? INITIALIZED : UNINITIALIZED) {}
|
cannam@147
|
98 #else
|
cannam@147
|
99 Once(bool startInitialized = false);
|
cannam@147
|
100 ~Once();
|
cannam@147
|
101 #endif
|
cannam@147
|
102 KJ_DISALLOW_COPY(Once);
|
cannam@147
|
103
|
cannam@147
|
104 class Initializer {
|
cannam@147
|
105 public:
|
cannam@147
|
106 virtual void run() = 0;
|
cannam@147
|
107 };
|
cannam@147
|
108
|
cannam@147
|
109 void runOnce(Initializer& init);
|
cannam@147
|
110
|
cannam@147
|
111 #if _WIN32 // TODO(perf): Can we make this inline on win32 somehow?
|
cannam@147
|
112 bool isInitialized() noexcept;
|
cannam@147
|
113
|
cannam@147
|
114 #else
|
cannam@147
|
115 inline bool isInitialized() noexcept {
|
cannam@147
|
116 // Fast path check to see if runOnce() would simply return immediately.
|
cannam@147
|
117 #if KJ_USE_FUTEX
|
cannam@147
|
118 return __atomic_load_n(&futex, __ATOMIC_ACQUIRE) == INITIALIZED;
|
cannam@147
|
119 #else
|
cannam@147
|
120 return __atomic_load_n(&state, __ATOMIC_ACQUIRE) == INITIALIZED;
|
cannam@147
|
121 #endif
|
cannam@147
|
122 }
|
cannam@147
|
123 #endif
|
cannam@147
|
124
|
cannam@147
|
125 void reset();
|
cannam@147
|
126 // Returns the state from initialized to uninitialized. It is an error to call this when
|
cannam@147
|
127 // not already initialized, or when runOnce() or isInitialized() might be called concurrently in
|
cannam@147
|
128 // another thread.
|
cannam@147
|
129
|
cannam@147
|
130 private:
|
cannam@147
|
131 #if KJ_USE_FUTEX
|
cannam@147
|
132 uint futex;
|
cannam@147
|
133
|
cannam@147
|
134 enum State {
|
cannam@147
|
135 UNINITIALIZED,
|
cannam@147
|
136 INITIALIZING,
|
cannam@147
|
137 INITIALIZING_WITH_WAITERS,
|
cannam@147
|
138 INITIALIZED
|
cannam@147
|
139 };
|
cannam@147
|
140
|
cannam@147
|
141 #elif _WIN32
|
cannam@147
|
142 uintptr_t initOnce; // Actually an INIT_ONCE, but don't want to #include <windows.h> in header.
|
cannam@147
|
143
|
cannam@147
|
144 #else
|
cannam@147
|
145 enum State {
|
cannam@147
|
146 UNINITIALIZED,
|
cannam@147
|
147 INITIALIZED
|
cannam@147
|
148 };
|
cannam@147
|
149 State state;
|
cannam@147
|
150 pthread_mutex_t mutex;
|
cannam@147
|
151 #endif
|
cannam@147
|
152 };
|
cannam@147
|
153
|
cannam@147
|
154 } // namespace _ (private)
|
cannam@147
|
155
|
cannam@147
|
156 // =======================================================================================
|
cannam@147
|
157 // Public interface
|
cannam@147
|
158
|
cannam@147
|
159 template <typename T>
|
cannam@147
|
160 class Locked {
|
cannam@147
|
161 // Return type for `MutexGuarded<T>::lock()`. `Locked<T>` provides access to the bounded object
|
cannam@147
|
162 // and unlocks the mutex when it goes out of scope.
|
cannam@147
|
163
|
cannam@147
|
164 public:
|
cannam@147
|
165 KJ_DISALLOW_COPY(Locked);
|
cannam@147
|
166 inline Locked(): mutex(nullptr), ptr(nullptr) {}
|
cannam@147
|
167 inline Locked(Locked&& other): mutex(other.mutex), ptr(other.ptr) {
|
cannam@147
|
168 other.mutex = nullptr;
|
cannam@147
|
169 other.ptr = nullptr;
|
cannam@147
|
170 }
|
cannam@147
|
171 inline ~Locked() {
|
cannam@147
|
172 if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE);
|
cannam@147
|
173 }
|
cannam@147
|
174
|
cannam@147
|
175 inline Locked& operator=(Locked&& other) {
|
cannam@147
|
176 if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE);
|
cannam@147
|
177 mutex = other.mutex;
|
cannam@147
|
178 ptr = other.ptr;
|
cannam@147
|
179 other.mutex = nullptr;
|
cannam@147
|
180 other.ptr = nullptr;
|
cannam@147
|
181 return *this;
|
cannam@147
|
182 }
|
cannam@147
|
183
|
cannam@147
|
184 inline void release() {
|
cannam@147
|
185 if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE);
|
cannam@147
|
186 mutex = nullptr;
|
cannam@147
|
187 ptr = nullptr;
|
cannam@147
|
188 }
|
cannam@147
|
189
|
cannam@147
|
190 inline T* operator->() { return ptr; }
|
cannam@147
|
191 inline const T* operator->() const { return ptr; }
|
cannam@147
|
192 inline T& operator*() { return *ptr; }
|
cannam@147
|
193 inline const T& operator*() const { return *ptr; }
|
cannam@147
|
194 inline T* get() { return ptr; }
|
cannam@147
|
195 inline const T* get() const { return ptr; }
|
cannam@147
|
196 inline operator T*() { return ptr; }
|
cannam@147
|
197 inline operator const T*() const { return ptr; }
|
cannam@147
|
198
|
cannam@147
|
199 private:
|
cannam@147
|
200 _::Mutex* mutex;
|
cannam@147
|
201 T* ptr;
|
cannam@147
|
202
|
cannam@147
|
203 inline Locked(_::Mutex& mutex, T& value): mutex(&mutex), ptr(&value) {}
|
cannam@147
|
204
|
cannam@147
|
205 template <typename U>
|
cannam@147
|
206 friend class MutexGuarded;
|
cannam@147
|
207 };
|
cannam@147
|
208
|
cannam@147
|
209 template <typename T>
|
cannam@147
|
210 class MutexGuarded {
|
cannam@147
|
211 // An object of type T, bounded by a mutex. In order to access the object, you must lock it.
|
cannam@147
|
212 //
|
cannam@147
|
213 // Write locks are not "recursive" -- trying to lock again in a thread that already holds a lock
|
cannam@147
|
214 // will deadlock. Recursive write locks are usually a sign of bad design.
|
cannam@147
|
215 //
|
cannam@147
|
216 // Unfortunately, **READ LOCKS ARE NOT RECURSIVE** either. Common sense says they should be.
|
cannam@147
|
217 // But on many operating systems (BSD, OSX), recursively read-locking a pthread_rwlock is
|
cannam@147
|
218 // actually unsafe. The problem is that writers are "prioritized" over readers, so a read lock
|
cannam@147
|
219 // request will block if any write lock requests are outstanding. So, if thread A takes a read
|
cannam@147
|
220 // lock, thread B requests a write lock (and starts waiting), and then thread A tries to take
|
cannam@147
|
221 // another read lock recursively, the result is deadlock.
|
cannam@147
|
222
|
cannam@147
|
223 public:
|
cannam@147
|
224 template <typename... Params>
|
cannam@147
|
225 explicit MutexGuarded(Params&&... params);
|
cannam@147
|
226 // Initialize the mutex-bounded object by passing the given parameters to its constructor.
|
cannam@147
|
227
|
cannam@147
|
228 Locked<T> lockExclusive() const;
|
cannam@147
|
229 // Exclusively locks the object and returns it. The returned `Locked<T>` can be passed by
|
cannam@147
|
230 // move, similar to `Own<T>`.
|
cannam@147
|
231 //
|
cannam@147
|
232 // This method is declared `const` in accordance with KJ style rules which say that constness
|
cannam@147
|
233 // should be used to indicate thread-safety. It is safe to share a const pointer between threads,
|
cannam@147
|
234 // but it is not safe to share a mutable pointer. Since the whole point of MutexGuarded is to
|
cannam@147
|
235 // be shared between threads, its methods should be const, even though locking it produces a
|
cannam@147
|
236 // non-const pointer to the contained object.
|
cannam@147
|
237
|
cannam@147
|
238 Locked<const T> lockShared() const;
|
cannam@147
|
239 // Lock the value for shared access. Multiple shared locks can be taken concurrently, but cannot
|
cannam@147
|
240 // be held at the same time as a non-shared lock.
|
cannam@147
|
241
|
cannam@147
|
242 inline const T& getWithoutLock() const { return value; }
|
cannam@147
|
243 inline T& getWithoutLock() { return value; }
|
cannam@147
|
244 // Escape hatch for cases where some external factor guarantees that it's safe to get the
|
cannam@147
|
245 // value. You should treat these like const_cast -- be highly suspicious of any use.
|
cannam@147
|
246
|
cannam@147
|
247 inline const T& getAlreadyLockedShared() const;
|
cannam@147
|
248 inline T& getAlreadyLockedShared();
|
cannam@147
|
249 inline T& getAlreadyLockedExclusive() const;
|
cannam@147
|
250 // Like `getWithoutLock()`, but asserts that the lock is already held by the calling thread.
|
cannam@147
|
251
|
cannam@147
|
252 private:
|
cannam@147
|
253 mutable _::Mutex mutex;
|
cannam@147
|
254 mutable T value;
|
cannam@147
|
255 };
|
cannam@147
|
256
|
cannam@147
|
257 template <typename T>
|
cannam@147
|
258 class MutexGuarded<const T> {
|
cannam@147
|
259 // MutexGuarded cannot guard a const type. This would be pointless anyway, and would complicate
|
cannam@147
|
260 // the implementation of Locked<T>, which uses constness to decide what kind of lock it holds.
|
cannam@147
|
261 static_assert(sizeof(T) < 0, "MutexGuarded's type cannot be const.");
|
cannam@147
|
262 };
|
cannam@147
|
263
|
cannam@147
|
264 template <typename T>
|
cannam@147
|
265 class Lazy {
|
cannam@147
|
266 // A lazily-initialized value.
|
cannam@147
|
267
|
cannam@147
|
268 public:
|
cannam@147
|
269 template <typename Func>
|
cannam@147
|
270 T& get(Func&& init);
|
cannam@147
|
271 template <typename Func>
|
cannam@147
|
272 const T& get(Func&& init) const;
|
cannam@147
|
273 // The first thread to call get() will invoke the given init function to construct the value.
|
cannam@147
|
274 // Other threads will block until construction completes, then return the same value.
|
cannam@147
|
275 //
|
cannam@147
|
276 // `init` is a functor(typically a lambda) which takes `SpaceFor<T>&` as its parameter and returns
|
cannam@147
|
277 // `Own<T>`. If `init` throws an exception, the exception is propagated out of that thread's
|
cannam@147
|
278 // call to `get()`, and subsequent calls behave as if `get()` hadn't been called at all yet --
|
cannam@147
|
279 // in other words, subsequent calls retry initialization until it succeeds.
|
cannam@147
|
280
|
cannam@147
|
281 private:
|
cannam@147
|
282 mutable _::Once once;
|
cannam@147
|
283 mutable SpaceFor<T> space;
|
cannam@147
|
284 mutable Own<T> value;
|
cannam@147
|
285
|
cannam@147
|
286 template <typename Func>
|
cannam@147
|
287 class InitImpl;
|
cannam@147
|
288 };
|
cannam@147
|
289
|
cannam@147
|
290 // =======================================================================================
|
cannam@147
|
291 // Inline implementation details
|
cannam@147
|
292
|
cannam@147
|
293 template <typename T>
|
cannam@147
|
294 template <typename... Params>
|
cannam@147
|
295 inline MutexGuarded<T>::MutexGuarded(Params&&... params)
|
cannam@147
|
296 : value(kj::fwd<Params>(params)...) {}
|
cannam@147
|
297
|
cannam@147
|
298 template <typename T>
|
cannam@147
|
299 inline Locked<T> MutexGuarded<T>::lockExclusive() const {
|
cannam@147
|
300 mutex.lock(_::Mutex::EXCLUSIVE);
|
cannam@147
|
301 return Locked<T>(mutex, value);
|
cannam@147
|
302 }
|
cannam@147
|
303
|
cannam@147
|
304 template <typename T>
|
cannam@147
|
305 inline Locked<const T> MutexGuarded<T>::lockShared() const {
|
cannam@147
|
306 mutex.lock(_::Mutex::SHARED);
|
cannam@147
|
307 return Locked<const T>(mutex, value);
|
cannam@147
|
308 }
|
cannam@147
|
309
|
cannam@147
|
310 template <typename T>
|
cannam@147
|
311 inline const T& MutexGuarded<T>::getAlreadyLockedShared() const {
|
cannam@147
|
312 #ifdef KJ_DEBUG
|
cannam@147
|
313 mutex.assertLockedByCaller(_::Mutex::SHARED);
|
cannam@147
|
314 #endif
|
cannam@147
|
315 return value;
|
cannam@147
|
316 }
|
cannam@147
|
317 template <typename T>
|
cannam@147
|
318 inline T& MutexGuarded<T>::getAlreadyLockedShared() {
|
cannam@147
|
319 #ifdef KJ_DEBUG
|
cannam@147
|
320 mutex.assertLockedByCaller(_::Mutex::SHARED);
|
cannam@147
|
321 #endif
|
cannam@147
|
322 return value;
|
cannam@147
|
323 }
|
cannam@147
|
324 template <typename T>
|
cannam@147
|
325 inline T& MutexGuarded<T>::getAlreadyLockedExclusive() const {
|
cannam@147
|
326 #ifdef KJ_DEBUG
|
cannam@147
|
327 mutex.assertLockedByCaller(_::Mutex::EXCLUSIVE);
|
cannam@147
|
328 #endif
|
cannam@147
|
329 return const_cast<T&>(value);
|
cannam@147
|
330 }
|
cannam@147
|
331
|
cannam@147
|
332 template <typename T>
|
cannam@147
|
333 template <typename Func>
|
cannam@147
|
334 class Lazy<T>::InitImpl: public _::Once::Initializer {
|
cannam@147
|
335 public:
|
cannam@147
|
336 inline InitImpl(const Lazy<T>& lazy, Func&& func): lazy(lazy), func(kj::fwd<Func>(func)) {}
|
cannam@147
|
337
|
cannam@147
|
338 void run() override {
|
cannam@147
|
339 lazy.value = func(lazy.space);
|
cannam@147
|
340 }
|
cannam@147
|
341
|
cannam@147
|
342 private:
|
cannam@147
|
343 const Lazy<T>& lazy;
|
cannam@147
|
344 Func func;
|
cannam@147
|
345 };
|
cannam@147
|
346
|
cannam@147
|
347 template <typename T>
|
cannam@147
|
348 template <typename Func>
|
cannam@147
|
349 inline T& Lazy<T>::get(Func&& init) {
|
cannam@147
|
350 if (!once.isInitialized()) {
|
cannam@147
|
351 InitImpl<Func> initImpl(*this, kj::fwd<Func>(init));
|
cannam@147
|
352 once.runOnce(initImpl);
|
cannam@147
|
353 }
|
cannam@147
|
354 return *value;
|
cannam@147
|
355 }
|
cannam@147
|
356
|
cannam@147
|
357 template <typename T>
|
cannam@147
|
358 template <typename Func>
|
cannam@147
|
359 inline const T& Lazy<T>::get(Func&& init) const {
|
cannam@147
|
360 if (!once.isInitialized()) {
|
cannam@147
|
361 InitImpl<Func> initImpl(*this, kj::fwd<Func>(init));
|
cannam@147
|
362 once.runOnce(initImpl);
|
cannam@147
|
363 }
|
cannam@147
|
364 return *value;
|
cannam@147
|
365 }
|
cannam@147
|
366
|
cannam@147
|
367 } // namespace kj
|
cannam@147
|
368
|
cannam@147
|
369 #endif // KJ_MUTEX_H_
|