Support: add llvm::unique_lock
authorDylan Noblesmith <nobled@dreamwidth.org>
Sat, 23 Aug 2014 23:07:14 +0000 (23:07 +0000)
committerDylan Noblesmith <nobled@dreamwidth.org>
Sat, 23 Aug 2014 23:07:14 +0000 (23:07 +0000)
Based on the STL class of the same name, it guards a mutex
while also allowing it to be unlocked conditionally before
destruction.

This eliminates the last naked usages of mutexes in LLVM and
clang.

It also uncovered and fixed a bug in callExternalFunction()
when compiled without USE_LIBFFI, where the mutex would never
be unlocked if the end of the function was reached.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216338 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/IR/ValueMap.h
include/llvm/Support/UniqueLock.h [new file with mode: 0644]
lib/ExecutionEngine/Interpreter/ExternalFunctions.cpp
lib/Support/Unix/Signals.inc

index 993694889a5a7dda7b5c9975caff032685cacc1e..aa8a29dc771d67133329ac26844ffa8e4f60fd91 100644 (file)
@@ -29,6 +29,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/ValueHandle.h"
 #include "llvm/Support/Mutex.h"
+#include "llvm/Support/UniqueLock.h"
 #include "llvm/Support/type_traits.h"
 #include <iterator>
 
@@ -216,12 +217,11 @@ public:
     // Make a copy that won't get changed even when *this is destroyed.
     ValueMapCallbackVH Copy(*this);
     typename Config::mutex_type *M = Config::getMutex(Copy.Map->Data);
+    unique_lock<typename Config::mutex_type> Guard;
     if (M)
-      M->lock();
+      Guard = unique_lock<typename Config::mutex_type>(*M);
     Config::onDelete(Copy.Map->Data, Copy.Unwrap());  // May destroy *this.
     Copy.Map->Map.erase(Copy);  // Definitely destroys *this.
-    if (M)
-      M->unlock();
   }
   void allUsesReplacedWith(Value *new_key) override {
     assert(isa<KeySansPointerT>(new_key) &&
@@ -229,8 +229,9 @@ public:
     // Make a copy that won't get changed even when *this is destroyed.
     ValueMapCallbackVH Copy(*this);
     typename Config::mutex_type *M = Config::getMutex(Copy.Map->Data);
+    unique_lock<typename Config::mutex_type> Guard;
     if (M)
-      M->lock();
+      Guard = unique_lock<typename Config::mutex_type>(*M);
 
     KeyT typed_new_key = cast<KeySansPointerT>(new_key);
     // Can destroy *this:
@@ -245,8 +246,6 @@ public:
         Copy.Map->insert(std::make_pair(typed_new_key, Target));
       }
     }
-    if (M)
-      M->unlock();
   }
 };
 
diff --git a/include/llvm/Support/UniqueLock.h b/include/llvm/Support/UniqueLock.h
new file mode 100644 (file)
index 0000000..5a4c273
--- /dev/null
@@ -0,0 +1,67 @@
+//===-- Support/UniqueLock.h - Acquire/Release Mutex In Scope ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a guard for a block of code that ensures a Mutex is locked
+// upon construction and released upon destruction.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_UNIQUE_LOCK_H
+#define LLVM_SUPPORT_UNIQUE_LOCK_H
+
+#include "llvm/Support/Mutex.h"
+
+namespace llvm {
+  /// A pared-down imitation of std::unique_lock from C++11. Contrary to the
+  /// name, it's really more of a wrapper for a lock. It may or may not have
+  /// an associated mutex, which is guaranteed to be locked upon creation
+  /// and unlocked after destruction. unique_lock can also unlock the mutex
+  /// and re-lock it freely during its lifetime.
+  /// @brief Guard a section of code with a mutex.
+  template<typename MutexT>
+  class unique_lock {
+    MutexT *M;
+    bool locked;
+
+    unique_lock(const unique_lock &) LLVM_DELETED_FUNCTION;
+    void operator=(const unique_lock &) LLVM_DELETED_FUNCTION;
+  public:
+    unique_lock() : M(nullptr), locked(false) {}
+    explicit unique_lock(MutexT &m) : M(&m), locked(true) { M->lock(); }
+
+    void operator=(unique_lock &&o) {
+      if (owns_lock())
+        M->unlock();
+      M = o.M;
+      locked = o.locked;
+      o.M = nullptr;
+      o.locked = false;
+    }
+
+    ~unique_lock() { if (owns_lock()) M->unlock(); }
+
+    void lock() {
+      assert(!locked && "mutex already locked!");
+      assert(M && "no associated mutex!");
+      M->lock();
+      locked = true;
+    }
+
+    void unlock() {
+      assert(locked && "unlocking a mutex that isn't locked!");
+      assert(M && "no associated mutex!");
+      M->unlock();
+      locked = false;
+    }
+
+    bool owns_lock() { return locked; }
+  };
+}
+
+#endif // LLVM_SUPPORT_UNIQUE_LOCK_H
index e4b89c60b4607e3c0b7f48f4aa7e68933aab5282..0fdcc5fd36108adc85a519e35b0536acb4c62645 100644 (file)
@@ -28,6 +28,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Mutex.h"
+#include "llvm/Support/UniqueLock.h"
 #include <cmath>
 #include <csignal>
 #include <cstdio>
@@ -248,14 +249,14 @@ GenericValue Interpreter::callExternalFunction(Function *F,
                                      const std::vector<GenericValue> &ArgVals) {
   TheInterpreter = this;
 
-  FunctionsLock->lock();
+  unique_lock<sys::Mutex> Guard(*FunctionsLock);
 
   // Do a lookup to see if the function is in our cache... this should just be a
   // deferred annotation!
   std::map<const Function *, ExFunc>::iterator FI = ExportedFunctions->find(F);
   if (ExFunc Fn = (FI == ExportedFunctions->end()) ? lookupFunction(F)
                                                    : FI->second) {
-    FunctionsLock->unlock();
+    Guard.unlock();
     return Fn(F->getFunctionType(), ArgVals);
   }
 
@@ -273,7 +274,7 @@ GenericValue Interpreter::callExternalFunction(Function *F,
     RawFn = RF->second;
   }
 
-  FunctionsLock->unlock();
+  Guard.unlock();
 
   GenericValue Result;
   if (RawFn != 0 && ffiInvoke(RawFn, F, ArgVals, getDataLayout(), Result))
index 34c39c9ad920fdfda891abf591244d35a8fbde70..c820553665d2cec8d548018c4571f4c8a1886638 100644 (file)
@@ -15,6 +15,7 @@
 #include "Unix.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Mutex.h"
+#include "llvm/Support/UniqueLock.h"
 #include <algorithm>
 #include <string>
 #include <vector>
@@ -162,25 +163,25 @@ static RETSIGTYPE SignalHandler(int Sig) {
   sigfillset(&SigMask);
   sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
 
-  SignalsMutex.lock();
-  RemoveFilesToRemove();
-
-  if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
-    if (InterruptFunction) {
-      void (*IF)() = InterruptFunction;
-      SignalsMutex.unlock();
-      InterruptFunction = nullptr;
-      IF();        // run the interrupt function.
+  {
+    unique_lock<SmartMutex<true>> Guard(SignalsMutex);
+    RemoveFilesToRemove();
+
+    if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) {
+      if (InterruptFunction) {
+        void (*IF)() = InterruptFunction;
+        Guard.unlock();
+        InterruptFunction = nullptr;
+        IF();        // run the interrupt function.
+        return;
+      }
+
+      Guard.unlock();
+      raise(Sig);   // Execute the default handler.
       return;
-    }
-
-    SignalsMutex.unlock();
-    raise(Sig);   // Execute the default handler.
-    return;
+   }
   }
 
-  SignalsMutex.unlock();
-
   // Otherwise if it is a fault (like SEGV) run any handler.
   for (unsigned i = 0, e = CallBacksToRun.size(); i != e; ++i)
     CallBacksToRun[i].first(CallBacksToRun[i].second);