Index: llvm/trunk/include/llvm/Support/RWMutex.h =================================================================== --- llvm/trunk/include/llvm/Support/RWMutex.h +++ llvm/trunk/include/llvm/Support/RWMutex.h @@ -19,90 +19,181 @@ #include #include +// std::shared_timed_mutex is only availble on macOS 10.12 and later. +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + defined(__MAC_10_12) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < __MAC_10_12 +#define USE_RW_MUTEX_IMPL +#endif + namespace llvm { namespace sys { - /// SmartMutex - An R/W mutex with a compile time constant parameter that - /// indicates whether this mutex should become a no-op when we're not - /// running in multithreaded mode. - template - class SmartRWMutex { - // shared_mutex (C++17) is more efficient than shared_timed_mutex (C++14) - // on Windows and always available on MSVC. +#if defined(USE_RW_MUTEX_IMPL) +/// Platform agnostic RWMutex class. +class RWMutexImpl { + /// @name Constructors + /// @{ +public: + /// Initializes the lock but doesn't acquire it. + /// Default Constructor. + explicit RWMutexImpl(); + + /// @} + /// @name Do Not Implement + /// @{ + RWMutexImpl(const RWMutexImpl &original) = delete; + RWMutexImpl &operator=(const RWMutexImpl &) = delete; + /// @} + + /// Releases and removes the lock + /// Destructor + ~RWMutexImpl(); + + /// @} + /// @name Methods + /// @{ +public: + /// Attempts to unconditionally acquire the lock in reader mode. If the + /// lock is held by a writer, this method will wait until it can acquire + /// the lock. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally acquire the lock in reader mode. + bool lock_shared(); + + /// Attempts to release the lock in reader mode. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally release the lock in reader mode. + bool unlock_shared(); + + /// Attempts to unconditionally acquire the lock in reader mode. If the + /// lock is held by any readers, this method will wait until it can + /// acquire the lock. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally acquire the lock in writer mode. + bool lock(); + + /// Attempts to release the lock in writer mode. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally release the lock in write mode. + bool unlock(); + + //@} + /// @name Platform Dependent Data + /// @{ +private: +#if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0 + void *data_ = nullptr; ///< We don't know what the data will be +#endif +}; +#endif + +/// SmartMutex - An R/W mutex with a compile time constant parameter that +/// indicates whether this mutex should become a no-op when we're not +/// running in multithreaded mode. +template class SmartRWMutex { + // shared_mutex (C++17) is more efficient than shared_timed_mutex (C++14) + // on Windows and always available on MSVC. #if defined(_MSC_VER) || __cplusplus > 201402L - std::shared_mutex impl; + std::shared_mutex impl; +#else +#if !defined(USE_RW_MUTEX_IMPL) + std::shared_timed_mutex impl; #else - std::shared_timed_mutex impl; + RWMutexImpl impl; #endif - unsigned readers = 0; - unsigned writers = 0; +#endif + unsigned readers = 0; + unsigned writers = 0; - public: - bool lock_shared() { - if (!mt_only || llvm_is_multithreaded()) { - impl.lock_shared(); - return true; - } - - // Single-threaded debugging code. This would be racy in multithreaded - // mode, but provides not sanity checks in single threaded mode. - ++readers; - return true; - } - - bool unlock_shared() { - if (!mt_only || llvm_is_multithreaded()) { - impl.unlock_shared(); - return true; - } - - // Single-threaded debugging code. This would be racy in multithreaded - // mode, but provides not sanity checks in single threaded mode. - assert(readers > 0 && "Reader lock not acquired before release!"); - --readers; - return true; - } - - bool lock() { - if (!mt_only || llvm_is_multithreaded()) { - impl.lock(); - return true; - } - - // Single-threaded debugging code. This would be racy in multithreaded - // mode, but provides not sanity checks in single threaded mode. - assert(writers == 0 && "Writer lock already acquired!"); - ++writers; - return true; - } - - bool unlock() { - if (!mt_only || llvm_is_multithreaded()) { - impl.unlock(); - return true; - } - - // Single-threaded debugging code. This would be racy in multithreaded - // mode, but provides not sanity checks in single threaded mode. - assert(writers == 1 && "Writer lock not acquired before release!"); - --writers; - return true; - } - }; - - typedef SmartRWMutex RWMutex; - - /// ScopedReader - RAII acquisition of a reader lock - template - using SmartScopedReader = const std::shared_lock>; - - typedef SmartScopedReader ScopedReader; - - /// ScopedWriter - RAII acquisition of a writer lock - template - using SmartScopedWriter = std::lock_guard>; +public: + bool lock_shared() { + if (!mt_only || llvm_is_multithreaded()) { + impl.lock_shared(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not sanity checks in single threaded mode. + ++readers; + return true; + } + + bool unlock_shared() { + if (!mt_only || llvm_is_multithreaded()) { + impl.unlock_shared(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not sanity checks in single threaded mode. + assert(readers > 0 && "Reader lock not acquired before release!"); + --readers; + return true; + } + + bool lock() { + if (!mt_only || llvm_is_multithreaded()) { + impl.lock(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not sanity checks in single threaded mode. + assert(writers == 0 && "Writer lock already acquired!"); + ++writers; + return true; + } + + bool unlock() { + if (!mt_only || llvm_is_multithreaded()) { + impl.unlock(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not sanity checks in single threaded mode. + assert(writers == 1 && "Writer lock not acquired before release!"); + --writers; + return true; + } +}; + +typedef SmartRWMutex RWMutex; + +/// ScopedReader - RAII acquisition of a reader lock +#if !defined(USE_RW_MUTEX_IMPL) +template +using SmartScopedReader = const std::shared_lock>; +#else +template struct SmartScopedReader { + SmartRWMutex &mutex; - typedef SmartScopedWriter ScopedWriter; + explicit SmartScopedReader(SmartRWMutex &m) : mutex(m) { + mutex.lock_shared(); + } + + ~SmartScopedReader() { mutex.unlock_shared(); } +}; +#endif +typedef SmartScopedReader ScopedReader; + +/// ScopedWriter - RAII acquisition of a writer lock +#if !defined(USE_RW_MUTEX_IMPL) +template +using SmartScopedWriter = std::lock_guard>; +#else +template struct SmartScopedWriter { + SmartRWMutex &mutex; + + explicit SmartScopedWriter(SmartRWMutex &m) : mutex(m) { + mutex.lock(); + } + + ~SmartScopedWriter() { mutex.unlock(); } +}; +#endif +typedef SmartScopedWriter ScopedWriter; } // end namespace sys } // end namespace llvm Index: llvm/trunk/lib/Support/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Support/CMakeLists.txt +++ llvm/trunk/lib/Support/CMakeLists.txt @@ -173,6 +173,7 @@ Path.cpp Process.cpp Program.cpp + RWMutex.cpp Signals.cpp TargetRegistry.cpp ThreadLocal.cpp Index: llvm/trunk/lib/Support/RWMutex.cpp =================================================================== --- llvm/trunk/lib/Support/RWMutex.cpp +++ llvm/trunk/lib/Support/RWMutex.cpp @@ -0,0 +1,136 @@ +//===- RWMutex.cpp - Reader/Writer Mutual Exclusion Lock --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the llvm::sys::RWMutex class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Allocator.h" +#include "llvm/Support/RWMutex.h" +#include "llvm/Config/config.h" + +#if defined(USE_RW_MUTEX_IMPL) +using namespace llvm; +using namespace sys; + +#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 +// Define all methods as no-ops if threading is explicitly disabled + +RWMutexImpl::RWMutexImpl() = default; +RWMutexImpl::~RWMutexImpl() = default; + +bool RWMutexImpl::lock_shared() { return true; } +bool RWMutexImpl::unlock_shared() { return true; } +bool RWMutexImpl::lock() { return true; } +bool RWMutexImpl::unlock() { return true; } + +#else + +#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_RWLOCK_INIT) + +#include +#include +#include + +// Construct a RWMutex using pthread calls +RWMutexImpl::RWMutexImpl() +{ + // Declare the pthread_rwlock data structures + pthread_rwlock_t* rwlock = + static_cast(safe_malloc(sizeof(pthread_rwlock_t))); + +#ifdef __APPLE__ + // Workaround a bug/mis-feature in Darwin's pthread_rwlock_init. + bzero(rwlock, sizeof(pthread_rwlock_t)); +#endif + + // Initialize the rwlock + int errorcode = pthread_rwlock_init(rwlock, nullptr); + (void)errorcode; + assert(errorcode == 0); + + // Assign the data member + data_ = rwlock; +} + +// Destruct a RWMutex +RWMutexImpl::~RWMutexImpl() +{ + pthread_rwlock_t* rwlock = static_cast(data_); + assert(rwlock != nullptr); + pthread_rwlock_destroy(rwlock); + free(rwlock); +} + +bool +RWMutexImpl::lock_shared() +{ + pthread_rwlock_t* rwlock = static_cast(data_); + assert(rwlock != nullptr); + + int errorcode = pthread_rwlock_rdlock(rwlock); + return errorcode == 0; +} + +bool +RWMutexImpl::unlock_shared() +{ + pthread_rwlock_t* rwlock = static_cast(data_); + assert(rwlock != nullptr); + + int errorcode = pthread_rwlock_unlock(rwlock); + return errorcode == 0; +} + +bool +RWMutexImpl::lock() +{ + pthread_rwlock_t* rwlock = static_cast(data_); + assert(rwlock != nullptr); + + int errorcode = pthread_rwlock_wrlock(rwlock); + return errorcode == 0; +} + +bool +RWMutexImpl::unlock() +{ + pthread_rwlock_t* rwlock = static_cast(data_); + assert(rwlock != nullptr); + + int errorcode = pthread_rwlock_unlock(rwlock); + return errorcode == 0; +} + +#else + +RWMutexImpl::RWMutexImpl() : data_(new MutexImpl(false)) { } + +RWMutexImpl::~RWMutexImpl() { + delete static_cast(data_); +} + +bool RWMutexImpl::lock_shared() { + return static_cast(data_)->acquire(); +} + +bool RWMutexImpl::unlock_shared() { + return static_cast(data_)->release(); +} + +bool RWMutexImpl::lock() { + return static_cast(data_)->acquire(); +} + +bool RWMutexImpl::unlock() { + return static_cast(data_)->release(); +} + +#endif +#endif +#endif