annotate win32-mingw/include/kj/mutex.h @ 163:5cc1366da2e9

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