From: Jonas Devlieghere Date: Thu, 15 Aug 2019 23:07:20 +0000 (+0000) Subject: [Support] Re-introduce the RWMutexImpl for macOS < 10.12 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=24baf1839a0a76ebe6cb0eaf0f14ce756f8d3cd1;p=llvm [Support] Re-introduce the RWMutexImpl for macOS < 10.12 In r369018, Benjamin replaced the custom RWMutex implementation with their C++14 counterpart. Unfortunately, std::shared_timed_mutex is only available on macOS 10.12 and later. This prevents LLVM from compiling even on newer versions of the OS when you have an older deployment target. This patch reintroduced the old RWMutexImpl but guards it by the macOS availability macro. Differential revision: https://reviews.llvm.org/D66313 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@369064 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/Support/RWMutex.h b/include/llvm/Support/RWMutex.h index d4587772ac6..b8e13fca976 100644 --- a/include/llvm/Support/RWMutex.h +++ b/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 + RWMutexImpl impl; +#endif +#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 +#if !defined(USE_RW_MUTEX_IMPL) +template +using SmartScopedReader = const std::shared_lock>; +#else +template struct SmartScopedReader { + SmartRWMutex &mutex; + + 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 - std::shared_timed_mutex impl; +template struct SmartScopedWriter { + SmartRWMutex &mutex; + + explicit SmartScopedWriter(SmartRWMutex &m) : mutex(m) { + mutex.lock(); + } + + ~SmartScopedWriter() { mutex.unlock(); } +}; #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>; - - typedef SmartScopedWriter ScopedWriter; +typedef SmartScopedWriter ScopedWriter; } // end namespace sys } // end namespace llvm diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index f75cb902815..013e6e6359f 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -173,6 +173,7 @@ add_llvm_library(LLVMSupport Path.cpp Process.cpp Program.cpp + RWMutex.cpp Signals.cpp TargetRegistry.cpp ThreadLocal.cpp diff --git a/lib/Support/RWMutex.cpp b/lib/Support/RWMutex.cpp new file mode 100644 index 00000000000..f89e0ed6d1b --- /dev/null +++ b/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