]> granicus.if.org Git - llvm/commitdiff
[Support] Re-introduce the RWMutexImpl for macOS < 10.12
authorJonas Devlieghere <jonas@devlieghere.com>
Thu, 15 Aug 2019 23:07:20 +0000 (23:07 +0000)
committerJonas Devlieghere <jonas@devlieghere.com>
Thu, 15 Aug 2019 23:07:20 +0000 (23:07 +0000)
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

include/llvm/Support/RWMutex.h
lib/Support/CMakeLists.txt
lib/Support/RWMutex.cpp [new file with mode: 0644]

index d4587772ac64877a505053c080b3fc88d97fe527..b8e13fca9766f724b071d3e9d69c20523ac7ddf7 100644 (file)
 #include <mutex>
 #include <shared_mutex>
 
+// 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<bool mt_only>
-    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 <bool mt_only> 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<false> RWMutex;
+
+/// ScopedReader - RAII acquisition of a reader lock
+#if !defined(USE_RW_MUTEX_IMPL)
+template <bool mt_only>
+using SmartScopedReader = const std::shared_lock<SmartRWMutex<mt_only>>;
+#else
+template <bool mt_only> struct SmartScopedReader {
+  SmartRWMutex<mt_only> &mutex;
+
+  explicit SmartScopedReader(SmartRWMutex<mt_only> &m) : mutex(m) {
+    mutex.lock_shared();
+  }
+
+  ~SmartScopedReader() { mutex.unlock_shared(); }
+};
+#endif
+typedef SmartScopedReader<false> ScopedReader;
+
+/// ScopedWriter - RAII acquisition of a writer lock
+#if !defined(USE_RW_MUTEX_IMPL)
+template <bool mt_only>
+using SmartScopedWriter = std::lock_guard<SmartRWMutex<mt_only>>;
 #else
-      std::shared_timed_mutex impl;
+template <bool mt_only> struct SmartScopedWriter {
+  SmartRWMutex<mt_only> &mutex;
+
+  explicit SmartScopedWriter(SmartRWMutex<mt_only> &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<false> RWMutex;
-
-    /// ScopedReader - RAII acquisition of a reader lock
-    template<bool mt_only>
-    using SmartScopedReader = const std::shared_lock<SmartRWMutex<mt_only>>;
-
-    typedef SmartScopedReader<false> ScopedReader;
-
-    /// ScopedWriter - RAII acquisition of a writer lock
-    template<bool mt_only>
-    using SmartScopedWriter = std::lock_guard<SmartRWMutex<mt_only>>;
-
-    typedef SmartScopedWriter<false> ScopedWriter;
+typedef SmartScopedWriter<false> ScopedWriter;
 
 } // end namespace sys
 } // end namespace llvm
index f75cb902815322ddba07645b227f0b988e9d2a3b..013e6e6359f1de7295a56bd50cbf03fa42edc7da 100644 (file)
@@ -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 (file)
index 0000000..f89e0ed
--- /dev/null
@@ -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 <cassert>
+#include <cstdlib>
+#include <pthread.h>
+
+// Construct a RWMutex using pthread calls
+RWMutexImpl::RWMutexImpl()
+{
+  // Declare the pthread_rwlock data structures
+  pthread_rwlock_t* rwlock =
+    static_cast<pthread_rwlock_t*>(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<pthread_rwlock_t*>(data_);
+  assert(rwlock != nullptr);
+  pthread_rwlock_destroy(rwlock);
+  free(rwlock);
+}
+
+bool
+RWMutexImpl::lock_shared()
+{
+  pthread_rwlock_t* rwlock = static_cast<pthread_rwlock_t*>(data_);
+  assert(rwlock != nullptr);
+
+  int errorcode = pthread_rwlock_rdlock(rwlock);
+  return errorcode == 0;
+}
+
+bool
+RWMutexImpl::unlock_shared()
+{
+  pthread_rwlock_t* rwlock = static_cast<pthread_rwlock_t*>(data_);
+  assert(rwlock != nullptr);
+
+  int errorcode = pthread_rwlock_unlock(rwlock);
+  return errorcode == 0;
+}
+
+bool
+RWMutexImpl::lock()
+{
+  pthread_rwlock_t* rwlock = static_cast<pthread_rwlock_t*>(data_);
+  assert(rwlock != nullptr);
+
+  int errorcode = pthread_rwlock_wrlock(rwlock);
+  return errorcode == 0;
+}
+
+bool
+RWMutexImpl::unlock()
+{
+  pthread_rwlock_t* rwlock = static_cast<pthread_rwlock_t*>(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<MutexImpl *>(data_);
+}
+
+bool RWMutexImpl::lock_shared() {
+  return static_cast<MutexImpl *>(data_)->acquire();
+}
+
+bool RWMutexImpl::unlock_shared() {
+  return static_cast<MutexImpl *>(data_)->release();
+}
+
+bool RWMutexImpl::lock() {
+  return static_cast<MutexImpl *>(data_)->acquire();
+}
+
+bool RWMutexImpl::unlock() {
+  return static_cast<MutexImpl *>(data_)->release();
+}
+
+#endif
+#endif
+#endif