annotate win64-msvc/include/kj/mutex.h @ 62:0994c39f1e94

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