Mercurial > hg > sv-dependency-builds
diff osx/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 | 3ab5a40c4e3b |
children |
line wrap: on
line diff
--- a/osx/include/kj/mutex.h Mon Mar 06 13:29:58 2017 +0000 +++ b/osx/include/kj/mutex.h Mon May 22 10:01:37 2017 +0100 @@ -1,369 +1,369 @@ -// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors -// Licensed under the MIT License: -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef KJ_MUTEX_H_ -#define KJ_MUTEX_H_ - -#if defined(__GNUC__) && !KJ_HEADER_WARNINGS -#pragma GCC system_header -#endif - -#include "memory.h" -#include <inttypes.h> - -#if __linux__ && !defined(KJ_USE_FUTEX) -#define KJ_USE_FUTEX 1 -#endif - -#if !KJ_USE_FUTEX && !_WIN32 -// On Linux we use futex. On other platforms we wrap pthreads. -// TODO(someday): Write efficient low-level locking primitives for other platforms. -#include <pthread.h> -#endif - -namespace kj { - -// ======================================================================================= -// Private details -- public interfaces follow below. - -namespace _ { // private - -class Mutex { - // Internal implementation details. See `MutexGuarded<T>`. - -public: - Mutex(); - ~Mutex(); - KJ_DISALLOW_COPY(Mutex); - - enum Exclusivity { - EXCLUSIVE, - SHARED - }; - - void lock(Exclusivity exclusivity); - void unlock(Exclusivity exclusivity); - - void assertLockedByCaller(Exclusivity exclusivity); - // In debug mode, assert that the mutex is locked by the calling thread, or if that is - // non-trivial, assert that the mutex is locked (which should be good enough to catch problems - // in unit tests). In non-debug builds, do nothing. - -private: -#if KJ_USE_FUTEX - uint futex; - // bit 31 (msb) = set if exclusive lock held - // bit 30 (msb) = set if threads are waiting for exclusive lock - // bits 0-29 = count of readers; If an exclusive lock is held, this is the count of threads - // waiting for a read lock, otherwise it is the count of threads that currently hold a read - // lock. - - static constexpr uint EXCLUSIVE_HELD = 1u << 31; - static constexpr uint EXCLUSIVE_REQUESTED = 1u << 30; - static constexpr uint SHARED_COUNT_MASK = EXCLUSIVE_REQUESTED - 1; - -#elif _WIN32 - uintptr_t srwLock; // Actually an SRWLOCK, but don't want to #include <windows.h> in header. - -#else - mutable pthread_rwlock_t mutex; -#endif -}; - -class Once { - // Internal implementation details. See `Lazy<T>`. - -public: -#if KJ_USE_FUTEX - inline Once(bool startInitialized = false) - : futex(startInitialized ? INITIALIZED : UNINITIALIZED) {} -#else - Once(bool startInitialized = false); - ~Once(); -#endif - KJ_DISALLOW_COPY(Once); - - class Initializer { - public: - virtual void run() = 0; - }; - - void runOnce(Initializer& init); - -#if _WIN32 // TODO(perf): Can we make this inline on win32 somehow? - bool isInitialized() noexcept; - -#else - inline bool isInitialized() noexcept { - // Fast path check to see if runOnce() would simply return immediately. -#if KJ_USE_FUTEX - return __atomic_load_n(&futex, __ATOMIC_ACQUIRE) == INITIALIZED; -#else - return __atomic_load_n(&state, __ATOMIC_ACQUIRE) == INITIALIZED; -#endif - } -#endif - - void reset(); - // Returns the state from initialized to uninitialized. It is an error to call this when - // not already initialized, or when runOnce() or isInitialized() might be called concurrently in - // another thread. - -private: -#if KJ_USE_FUTEX - uint futex; - - enum State { - UNINITIALIZED, - INITIALIZING, - INITIALIZING_WITH_WAITERS, - INITIALIZED - }; - -#elif _WIN32 - uintptr_t initOnce; // Actually an INIT_ONCE, but don't want to #include <windows.h> in header. - -#else - enum State { - UNINITIALIZED, - INITIALIZED - }; - State state; - pthread_mutex_t mutex; -#endif -}; - -} // namespace _ (private) - -// ======================================================================================= -// Public interface - -template <typename T> -class Locked { - // Return type for `MutexGuarded<T>::lock()`. `Locked<T>` provides access to the guarded object - // and unlocks the mutex when it goes out of scope. - -public: - KJ_DISALLOW_COPY(Locked); - inline Locked(): mutex(nullptr), ptr(nullptr) {} - inline Locked(Locked&& other): mutex(other.mutex), ptr(other.ptr) { - other.mutex = nullptr; - other.ptr = nullptr; - } - inline ~Locked() { - if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); - } - - inline Locked& operator=(Locked&& other) { - if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); - mutex = other.mutex; - ptr = other.ptr; - other.mutex = nullptr; - other.ptr = nullptr; - return *this; - } - - inline void release() { - if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); - mutex = nullptr; - ptr = nullptr; - } - - inline T* operator->() { return ptr; } - inline const T* operator->() const { return ptr; } - inline T& operator*() { return *ptr; } - inline const T& operator*() const { return *ptr; } - inline T* get() { return ptr; } - inline const T* get() const { return ptr; } - inline operator T*() { return ptr; } - inline operator const T*() const { return ptr; } - -private: - _::Mutex* mutex; - T* ptr; - - inline Locked(_::Mutex& mutex, T& value): mutex(&mutex), ptr(&value) {} - - template <typename U> - friend class MutexGuarded; -}; - -template <typename T> -class MutexGuarded { - // An object of type T, guarded by a mutex. In order to access the object, you must lock it. - // - // Write locks are not "recursive" -- trying to lock again in a thread that already holds a lock - // will deadlock. Recursive write locks are usually a sign of bad design. - // - // Unfortunately, **READ LOCKS ARE NOT RECURSIVE** either. Common sense says they should be. - // But on many operating systems (BSD, OSX), recursively read-locking a pthread_rwlock is - // actually unsafe. The problem is that writers are "prioritized" over readers, so a read lock - // request will block if any write lock requests are outstanding. So, if thread A takes a read - // lock, thread B requests a write lock (and starts waiting), and then thread A tries to take - // another read lock recursively, the result is deadlock. - -public: - template <typename... Params> - explicit MutexGuarded(Params&&... params); - // Initialize the mutex-guarded object by passing the given parameters to its constructor. - - Locked<T> lockExclusive() const; - // Exclusively locks the object and returns it. The returned `Locked<T>` can be passed by - // move, similar to `Own<T>`. - // - // This method is declared `const` in accordance with KJ style rules which say that constness - // should be used to indicate thread-safety. It is safe to share a const pointer between threads, - // but it is not safe to share a mutable pointer. Since the whole point of MutexGuarded is to - // be shared between threads, its methods should be const, even though locking it produces a - // non-const pointer to the contained object. - - Locked<const T> lockShared() const; - // Lock the value for shared access. Multiple shared locks can be taken concurrently, but cannot - // be held at the same time as a non-shared lock. - - inline const T& getWithoutLock() const { return value; } - inline T& getWithoutLock() { return value; } - // Escape hatch for cases where some external factor guarantees that it's safe to get the - // value. You should treat these like const_cast -- be highly suspicious of any use. - - inline const T& getAlreadyLockedShared() const; - inline T& getAlreadyLockedShared(); - inline T& getAlreadyLockedExclusive() const; - // Like `getWithoutLock()`, but asserts that the lock is already held by the calling thread. - -private: - mutable _::Mutex mutex; - mutable T value; -}; - -template <typename T> -class MutexGuarded<const T> { - // MutexGuarded cannot guard a const type. This would be pointless anyway, and would complicate - // the implementation of Locked<T>, which uses constness to decide what kind of lock it holds. - static_assert(sizeof(T) < 0, "MutexGuarded's type cannot be const."); -}; - -template <typename T> -class Lazy { - // A lazily-initialized value. - -public: - template <typename Func> - T& get(Func&& init); - template <typename Func> - const T& get(Func&& init) const; - // The first thread to call get() will invoke the given init function to construct the value. - // Other threads will block until construction completes, then return the same value. - // - // `init` is a functor(typically a lambda) which takes `SpaceFor<T>&` as its parameter and returns - // `Own<T>`. If `init` throws an exception, the exception is propagated out of that thread's - // call to `get()`, and subsequent calls behave as if `get()` hadn't been called at all yet -- - // in other words, subsequent calls retry initialization until it succeeds. - -private: - mutable _::Once once; - mutable SpaceFor<T> space; - mutable Own<T> value; - - template <typename Func> - class InitImpl; -}; - -// ======================================================================================= -// Inline implementation details - -template <typename T> -template <typename... Params> -inline MutexGuarded<T>::MutexGuarded(Params&&... params) - : value(kj::fwd<Params>(params)...) {} - -template <typename T> -inline Locked<T> MutexGuarded<T>::lockExclusive() const { - mutex.lock(_::Mutex::EXCLUSIVE); - return Locked<T>(mutex, value); -} - -template <typename T> -inline Locked<const T> MutexGuarded<T>::lockShared() const { - mutex.lock(_::Mutex::SHARED); - return Locked<const T>(mutex, value); -} - -template <typename T> -inline const T& MutexGuarded<T>::getAlreadyLockedShared() const { -#ifdef KJ_DEBUG - mutex.assertLockedByCaller(_::Mutex::SHARED); -#endif - return value; -} -template <typename T> -inline T& MutexGuarded<T>::getAlreadyLockedShared() { -#ifdef KJ_DEBUG - mutex.assertLockedByCaller(_::Mutex::SHARED); -#endif - return value; -} -template <typename T> -inline T& MutexGuarded<T>::getAlreadyLockedExclusive() const { -#ifdef KJ_DEBUG - mutex.assertLockedByCaller(_::Mutex::EXCLUSIVE); -#endif - return const_cast<T&>(value); -} - -template <typename T> -template <typename Func> -class Lazy<T>::InitImpl: public _::Once::Initializer { -public: - inline InitImpl(const Lazy<T>& lazy, Func&& func): lazy(lazy), func(kj::fwd<Func>(func)) {} - - void run() override { - lazy.value = func(lazy.space); - } - -private: - const Lazy<T>& lazy; - Func func; -}; - -template <typename T> -template <typename Func> -inline T& Lazy<T>::get(Func&& init) { - if (!once.isInitialized()) { - InitImpl<Func> initImpl(*this, kj::fwd<Func>(init)); - once.runOnce(initImpl); - } - return *value; -} - -template <typename T> -template <typename Func> -inline const T& Lazy<T>::get(Func&& init) const { - if (!once.isInitialized()) { - InitImpl<Func> initImpl(*this, kj::fwd<Func>(init)); - once.runOnce(initImpl); - } - return *value; -} - -} // namespace kj - -#endif // KJ_MUTEX_H_ +// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +// Licensed under the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef KJ_MUTEX_H_ +#define KJ_MUTEX_H_ + +#if defined(__GNUC__) && !KJ_HEADER_WARNINGS +#pragma GCC system_header +#endif + +#include "memory.h" +#include <inttypes.h> + +#if __linux__ && !defined(KJ_USE_FUTEX) +#define KJ_USE_FUTEX 1 +#endif + +#if !KJ_USE_FUTEX && !_WIN32 +// On Linux we use futex. On other platforms we wrap pthreads. +// TODO(someday): Write efficient low-level locking primitives for other platforms. +#include <pthread.h> +#endif + +namespace kj { + +// ======================================================================================= +// Private details -- public interfaces follow below. + +namespace _ { // private + +class Mutex { + // Internal implementation details. See `MutexGuarded<T>`. + +public: + Mutex(); + ~Mutex(); + KJ_DISALLOW_COPY(Mutex); + + enum Exclusivity { + EXCLUSIVE, + SHARED + }; + + void lock(Exclusivity exclusivity); + void unlock(Exclusivity exclusivity); + + void assertLockedByCaller(Exclusivity exclusivity); + // In debug mode, assert that the mutex is locked by the calling thread, or if that is + // non-trivial, assert that the mutex is locked (which should be good enough to catch problems + // in unit tests). In non-debug builds, do nothing. + +private: +#if KJ_USE_FUTEX + uint futex; + // bit 31 (msb) = set if exclusive lock held + // bit 30 (msb) = set if threads are waiting for exclusive lock + // bits 0-29 = count of readers; If an exclusive lock is held, this is the count of threads + // waiting for a read lock, otherwise it is the count of threads that currently hold a read + // lock. + + static constexpr uint EXCLUSIVE_HELD = 1u << 31; + static constexpr uint EXCLUSIVE_REQUESTED = 1u << 30; + static constexpr uint SHARED_COUNT_MASK = EXCLUSIVE_REQUESTED - 1; + +#elif _WIN32 + uintptr_t srwLock; // Actually an SRWLOCK, but don't want to #include <windows.h> in header. + +#else + mutable pthread_rwlock_t mutex; +#endif +}; + +class Once { + // Internal implementation details. See `Lazy<T>`. + +public: +#if KJ_USE_FUTEX + inline Once(bool startInitialized = false) + : futex(startInitialized ? INITIALIZED : UNINITIALIZED) {} +#else + Once(bool startInitialized = false); + ~Once(); +#endif + KJ_DISALLOW_COPY(Once); + + class Initializer { + public: + virtual void run() = 0; + }; + + void runOnce(Initializer& init); + +#if _WIN32 // TODO(perf): Can we make this inline on win32 somehow? + bool isInitialized() noexcept; + +#else + inline bool isInitialized() noexcept { + // Fast path check to see if runOnce() would simply return immediately. +#if KJ_USE_FUTEX + return __atomic_load_n(&futex, __ATOMIC_ACQUIRE) == INITIALIZED; +#else + return __atomic_load_n(&state, __ATOMIC_ACQUIRE) == INITIALIZED; +#endif + } +#endif + + void reset(); + // Returns the state from initialized to uninitialized. It is an error to call this when + // not already initialized, or when runOnce() or isInitialized() might be called concurrently in + // another thread. + +private: +#if KJ_USE_FUTEX + uint futex; + + enum State { + UNINITIALIZED, + INITIALIZING, + INITIALIZING_WITH_WAITERS, + INITIALIZED + }; + +#elif _WIN32 + uintptr_t initOnce; // Actually an INIT_ONCE, but don't want to #include <windows.h> in header. + +#else + enum State { + UNINITIALIZED, + INITIALIZED + }; + State state; + pthread_mutex_t mutex; +#endif +}; + +} // namespace _ (private) + +// ======================================================================================= +// Public interface + +template <typename T> +class Locked { + // Return type for `MutexGuarded<T>::lock()`. `Locked<T>` provides access to the bounded object + // and unlocks the mutex when it goes out of scope. + +public: + KJ_DISALLOW_COPY(Locked); + inline Locked(): mutex(nullptr), ptr(nullptr) {} + inline Locked(Locked&& other): mutex(other.mutex), ptr(other.ptr) { + other.mutex = nullptr; + other.ptr = nullptr; + } + inline ~Locked() { + if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); + } + + inline Locked& operator=(Locked&& other) { + if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); + mutex = other.mutex; + ptr = other.ptr; + other.mutex = nullptr; + other.ptr = nullptr; + return *this; + } + + inline void release() { + if (mutex != nullptr) mutex->unlock(isConst<T>() ? _::Mutex::SHARED : _::Mutex::EXCLUSIVE); + mutex = nullptr; + ptr = nullptr; + } + + inline T* operator->() { return ptr; } + inline const T* operator->() const { return ptr; } + inline T& operator*() { return *ptr; } + inline const T& operator*() const { return *ptr; } + inline T* get() { return ptr; } + inline const T* get() const { return ptr; } + inline operator T*() { return ptr; } + inline operator const T*() const { return ptr; } + +private: + _::Mutex* mutex; + T* ptr; + + inline Locked(_::Mutex& mutex, T& value): mutex(&mutex), ptr(&value) {} + + template <typename U> + friend class MutexGuarded; +}; + +template <typename T> +class MutexGuarded { + // An object of type T, bounded by a mutex. In order to access the object, you must lock it. + // + // Write locks are not "recursive" -- trying to lock again in a thread that already holds a lock + // will deadlock. Recursive write locks are usually a sign of bad design. + // + // Unfortunately, **READ LOCKS ARE NOT RECURSIVE** either. Common sense says they should be. + // But on many operating systems (BSD, OSX), recursively read-locking a pthread_rwlock is + // actually unsafe. The problem is that writers are "prioritized" over readers, so a read lock + // request will block if any write lock requests are outstanding. So, if thread A takes a read + // lock, thread B requests a write lock (and starts waiting), and then thread A tries to take + // another read lock recursively, the result is deadlock. + +public: + template <typename... Params> + explicit MutexGuarded(Params&&... params); + // Initialize the mutex-bounded object by passing the given parameters to its constructor. + + Locked<T> lockExclusive() const; + // Exclusively locks the object and returns it. The returned `Locked<T>` can be passed by + // move, similar to `Own<T>`. + // + // This method is declared `const` in accordance with KJ style rules which say that constness + // should be used to indicate thread-safety. It is safe to share a const pointer between threads, + // but it is not safe to share a mutable pointer. Since the whole point of MutexGuarded is to + // be shared between threads, its methods should be const, even though locking it produces a + // non-const pointer to the contained object. + + Locked<const T> lockShared() const; + // Lock the value for shared access. Multiple shared locks can be taken concurrently, but cannot + // be held at the same time as a non-shared lock. + + inline const T& getWithoutLock() const { return value; } + inline T& getWithoutLock() { return value; } + // Escape hatch for cases where some external factor guarantees that it's safe to get the + // value. You should treat these like const_cast -- be highly suspicious of any use. + + inline const T& getAlreadyLockedShared() const; + inline T& getAlreadyLockedShared(); + inline T& getAlreadyLockedExclusive() const; + // Like `getWithoutLock()`, but asserts that the lock is already held by the calling thread. + +private: + mutable _::Mutex mutex; + mutable T value; +}; + +template <typename T> +class MutexGuarded<const T> { + // MutexGuarded cannot guard a const type. This would be pointless anyway, and would complicate + // the implementation of Locked<T>, which uses constness to decide what kind of lock it holds. + static_assert(sizeof(T) < 0, "MutexGuarded's type cannot be const."); +}; + +template <typename T> +class Lazy { + // A lazily-initialized value. + +public: + template <typename Func> + T& get(Func&& init); + template <typename Func> + const T& get(Func&& init) const; + // The first thread to call get() will invoke the given init function to construct the value. + // Other threads will block until construction completes, then return the same value. + // + // `init` is a functor(typically a lambda) which takes `SpaceFor<T>&` as its parameter and returns + // `Own<T>`. If `init` throws an exception, the exception is propagated out of that thread's + // call to `get()`, and subsequent calls behave as if `get()` hadn't been called at all yet -- + // in other words, subsequent calls retry initialization until it succeeds. + +private: + mutable _::Once once; + mutable SpaceFor<T> space; + mutable Own<T> value; + + template <typename Func> + class InitImpl; +}; + +// ======================================================================================= +// Inline implementation details + +template <typename T> +template <typename... Params> +inline MutexGuarded<T>::MutexGuarded(Params&&... params) + : value(kj::fwd<Params>(params)...) {} + +template <typename T> +inline Locked<T> MutexGuarded<T>::lockExclusive() const { + mutex.lock(_::Mutex::EXCLUSIVE); + return Locked<T>(mutex, value); +} + +template <typename T> +inline Locked<const T> MutexGuarded<T>::lockShared() const { + mutex.lock(_::Mutex::SHARED); + return Locked<const T>(mutex, value); +} + +template <typename T> +inline const T& MutexGuarded<T>::getAlreadyLockedShared() const { +#ifdef KJ_DEBUG + mutex.assertLockedByCaller(_::Mutex::SHARED); +#endif + return value; +} +template <typename T> +inline T& MutexGuarded<T>::getAlreadyLockedShared() { +#ifdef KJ_DEBUG + mutex.assertLockedByCaller(_::Mutex::SHARED); +#endif + return value; +} +template <typename T> +inline T& MutexGuarded<T>::getAlreadyLockedExclusive() const { +#ifdef KJ_DEBUG + mutex.assertLockedByCaller(_::Mutex::EXCLUSIVE); +#endif + return const_cast<T&>(value); +} + +template <typename T> +template <typename Func> +class Lazy<T>::InitImpl: public _::Once::Initializer { +public: + inline InitImpl(const Lazy<T>& lazy, Func&& func): lazy(lazy), func(kj::fwd<Func>(func)) {} + + void run() override { + lazy.value = func(lazy.space); + } + +private: + const Lazy<T>& lazy; + Func func; +}; + +template <typename T> +template <typename Func> +inline T& Lazy<T>::get(Func&& init) { + if (!once.isInitialized()) { + InitImpl<Func> initImpl(*this, kj::fwd<Func>(init)); + once.runOnce(initImpl); + } + return *value; +} + +template <typename T> +template <typename Func> +inline const T& Lazy<T>::get(Func&& init) const { + if (!once.isInitialized()) { + InitImpl<Func> initImpl(*this, kj::fwd<Func>(init)); + once.runOnce(initImpl); + } + return *value; +} + +} // namespace kj + +#endif // KJ_MUTEX_H_