[llvm] r271558 - This is yet another attempt to re-instate r220932 as discussed in

Chandler Carruth via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 2 11:22:13 PDT 2016


Author: chandlerc
Date: Thu Jun  2 13:22:12 2016
New Revision: 271558

URL: http://llvm.org/viewvc/llvm-project?rev=271558&view=rev
Log:
This is yet another attempt to re-instate r220932 as discussed in
D19271.

Previous attempt was broken by NetBSD, so in this version I've made the
fallback path generic rather than Windows specific and sent both Windows
and NetBSD to it.

I've also re-formatted the code some, and used an exact clone of the
code in PassSupport.h for doing manual call-once using our atomics
rather than rolling a new one.

If this sticks, we can replace the fallback path for Windows with
a Windows-specific implementation that is more reliable.

Original commit message:
This patch adds an llvm_call_once which is a wrapper around
std::call_once on platforms where it is available and devoid
of bugs. The patch also migrates the ManagedStatic mutex to
be allocated using llvm_call_once.

These changes are philosophically equivalent to the changes
added in r219638, which were reverted due to a hang on Win32
which was the result of a bug in the Windows implementation
of std::call_once.

Differential Revision: http://reviews.llvm.org/D5922

Modified:
    llvm/trunk/include/llvm/Support/Threading.h
    llvm/trunk/lib/Support/ManagedStatic.cpp
    llvm/trunk/lib/Support/Threading.cpp

Modified: llvm/trunk/include/llvm/Support/Threading.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Threading.h?rev=271558&r1=271557&r2=271558&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Threading.h (original)
+++ llvm/trunk/include/llvm/Support/Threading.h Thu Jun  2 13:22:12 2016
@@ -15,6 +15,24 @@
 #ifndef LLVM_SUPPORT_THREADING_H
 #define LLVM_SUPPORT_THREADING_H
 
+#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
+#include <ciso646> // So we can check the C++ standard lib macros.
+
+// We use std::call_once on all Unix platforms except for NetBSD with
+// libstdc++. That platform has a bug they are working to fix, and they'll
+// remove the NetBSD checks once fixed.
+#if defined(LLVM_ON_UNIX) && !(defined(__NetBSD__) && !defined(_LIBCPP_VERSION))
+#define LLVM_THREADING_USE_STD_CALL_ONCE 1
+#else
+#define LLVM_THREADING_USE_STD_CALL_ONCE 0
+#endif
+
+#if LLVM_THREADING_USE_STD_CALL_ONCE
+#include <mutex>
+#else
+#include "llvm/Support/Atomic.h"
+#endif
+
 namespace llvm {
   /// Returns true if LLVM is compiled with support for multi-threading, and
   /// false otherwise.
@@ -34,6 +52,39 @@ namespace llvm {
   /// the thread stack.
   void llvm_execute_on_thread(void (*UserFn)(void*), void *UserData,
                               unsigned RequestedStackSize = 0);
+
+#if LLVM_THREADING_USE_STD_CALL_ONCE
+
+  typedef std::once_flag once_flag;
+
+  /// This macro is the only way you should define your once flag for LLVM's
+  /// call_once.
+#define LLVM_DEFINE_ONCE_FLAG(flag) static once_flag flag
+
+#else
+
+  enum InitStatus { Uninitialized = 0, Wait = 1, Done = 2 };
+  typedef volatile sys::cas_flag once_flag;
+
+  /// This macro is the only way you should define your once flag for LLVM's
+  /// call_once.
+#define LLVM_DEFINE_ONCE_FLAG(flag) static once_flag flag = Uninitialized
+
+#endif
+
+  /// \brief Execute the function specified as a parameter once.
+  ///
+  /// Typical usage:
+  /// \code
+  ///   void foo() {...};
+  ///   ...
+  ///   LLVM_DEFINE_ONCE_FLAG(flag);
+  ///   call_once(flag, foo);
+  /// \endcode
+  ///
+  /// \param flag Flag used for tracking whether or not this has run.
+  /// \param UserFn Function to call once.
+  void call_once(once_flag &flag, void (*UserFn)(void));
 }
 
 #endif

Modified: llvm/trunk/lib/Support/ManagedStatic.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/ManagedStatic.cpp?rev=271558&r1=271557&r2=271558&view=diff
==============================================================================
--- llvm/trunk/lib/Support/ManagedStatic.cpp (original)
+++ llvm/trunk/lib/Support/ManagedStatic.cpp Thu Jun  2 13:22:12 2016
@@ -16,16 +16,23 @@
 #include "llvm/Support/Atomic.h"
 #include "llvm/Support/Mutex.h"
 #include "llvm/Support/MutexGuard.h"
+#include "llvm/Support/Threading.h"
 #include <cassert>
 using namespace llvm;
 
 static const ManagedStaticBase *StaticList = nullptr;
+static sys::Mutex *ManagedStaticMutex = nullptr;
+LLVM_DEFINE_ONCE_FLAG(mutex_init_flag);
 
-static sys::Mutex& getManagedStaticMutex() {
+static void initializeMutex() {
+  ManagedStaticMutex = new sys::Mutex();
+}
+
+static sys::Mutex* getManagedStaticMutex() {
   // We need to use a function local static here, since this can get called
   // during a static constructor and we need to guarantee that it's initialized
   // correctly.
-  static sys::Mutex ManagedStaticMutex;
+  call_once(mutex_init_flag, initializeMutex);
   return ManagedStaticMutex;
 }
 
@@ -33,7 +40,7 @@ void ManagedStaticBase::RegisterManagedS
                                               void (*Deleter)(void*)) const {
   assert(Creator);
   if (llvm_is_multithreaded()) {
-    MutexGuard Lock(getManagedStaticMutex());
+    MutexGuard Lock(*getManagedStaticMutex());
 
     if (!Ptr) {
       void* tmp = Creator();
@@ -83,7 +90,7 @@ void ManagedStaticBase::destroy() const
 
 /// llvm_shutdown - Deallocate and destroy all ManagedStatic variables.
 void llvm::llvm_shutdown() {
-  MutexGuard Lock(getManagedStaticMutex());
+  MutexGuard Lock(*getManagedStaticMutex());
 
   while (StaticList)
     StaticList->destroy();

Modified: llvm/trunk/lib/Support/Threading.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Threading.cpp?rev=271558&r1=271557&r2=271558&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Threading.cpp (original)
+++ llvm/trunk/lib/Support/Threading.cpp Thu Jun  2 13:22:12 2016
@@ -16,6 +16,7 @@
 #include "llvm/Config/config.h"
 #include "llvm/Support/Atomic.h"
 #include "llvm/Support/Mutex.h"
+#include "llvm/Support/thread.h"
 #include <cassert>
 
 using namespace llvm;
@@ -110,3 +111,30 @@ void llvm::llvm_execute_on_thread(void (
 }
 
 #endif
+
+void llvm::call_once(once_flag &flag, void (*fptr)(void)) {
+#if LLVM_THREADING_USE_STD_CALL_ONCE
+  std::call_once(flag, fptr);
+#else
+  // For other platforms we use a generic (if brittle) version based on our
+  // atomics.
+  sys::cas_flag old_val = sys::CompareAndSwap(&flag, Wait, Uninitialized);
+  if (old_val == Uninitialized) {
+    fptr();
+    sys::MemoryFence();
+    TsanIgnoreWritesBegin();
+    TsanHappensBefore(&flag);
+    flag = Done;
+    TsanIgnoreWritesEnd();
+  } else {
+    // Wait until any thread doing the call has finished.
+    sys::cas_flag tmp = flag;
+    sys::MemoryFence();
+    while (tmp != Done) {
+      tmp = flag;
+      sys::MemoryFence();
+    }
+  }
+  TsanHappensAfter(&flag);
+#endif
+}




More information about the llvm-commits mailing list