asan/tsan: move blocking mutex from asan to sanitizer_common
authorDmitry Vyukov <dvyukov@google.com>
Mon, 14 Jan 2013 07:51:39 +0000 (07:51 +0000)
committerDmitry Vyukov <dvyukov@google.com>
Mon, 14 Jan 2013 07:51:39 +0000 (07:51 +0000)
llvm-svn: 172380

18 files changed:
compiler-rt/lib/asan/asan_allocator.cc
compiler-rt/lib/asan/asan_globals.cc
compiler-rt/lib/asan/asan_linux.cc
compiler-rt/lib/asan/asan_lock.h
compiler-rt/lib/asan/asan_mac.cc
compiler-rt/lib/asan/asan_rtl.cc
compiler-rt/lib/asan/asan_stats.cc
compiler-rt/lib/asan/asan_thread_registry.cc
compiler-rt/lib/asan/asan_thread_registry.h
compiler-rt/lib/asan/asan_win.cc
compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
compiler-rt/lib/sanitizer_common/sanitizer_linux.cc
compiler-rt/lib/sanitizer_common/sanitizer_mac.cc
compiler-rt/lib/sanitizer_common/sanitizer_mutex.h
compiler-rt/lib/sanitizer_common/sanitizer_win.cc
compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
compiler-rt/lib/tsan/Makefile.old
compiler-rt/lib/tsan/rtl/Makefile.old

index d94fb5ec9279818870eb4021d2d5bf8fb0f19b9b..30dd4ceddd88ce9fae1f5816c662e09e80e0d159 100644 (file)
@@ -29,7 +29,6 @@
 #if ASAN_ALLOCATOR_VERSION == 1
 #include "asan_interceptors.h"
 #include "asan_internal.h"
-#include "asan_lock.h"
 #include "asan_mapping.h"
 #include "asan_stats.h"
 #include "asan_report.h"
@@ -37,6 +36,7 @@
 #include "asan_thread_registry.h"
 #include "sanitizer/asan_interface.h"
 #include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_mutex.h"
 
 namespace __asan {
 
@@ -229,7 +229,7 @@ class MallocInfo {
     AsanChunk *m = 0;
     AsanChunk **fl = &free_lists_[size_class];
     {
-      ScopedLock lock(&mu_);
+      BlockingMutexLock lock(&mu_);
       for (uptr i = 0; i < n_chunks; i++) {
         if (!(*fl)) {
           *fl = GetNewChunks(size_class);
@@ -247,7 +247,7 @@ class MallocInfo {
   void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x,
                                        bool eat_free_lists) {
     CHECK(flags()->quarantine_size > 0);
-    ScopedLock lock(&mu_);
+    BlockingMutexLock lock(&mu_);
     AsanChunkFifoList *q = &x->quarantine_;
     if (q->size() > 0) {
       quarantine_.PushList(q);
@@ -271,18 +271,18 @@ class MallocInfo {
   }
 
   void BypassThreadLocalQuarantine(AsanChunk *chunk) {
-    ScopedLock lock(&mu_);
+    BlockingMutexLock lock(&mu_);
     quarantine_.Push(chunk);
   }
 
   AsanChunk *FindChunkByAddr(uptr addr) {
-    ScopedLock lock(&mu_);
+    BlockingMutexLock lock(&mu_);
     return FindChunkByAddrUnlocked(addr);
   }
 
   uptr AllocationSize(uptr ptr) {
     if (!ptr) return 0;
-    ScopedLock lock(&mu_);
+    BlockingMutexLock lock(&mu_);
 
     // Make sure this is our chunk and |ptr| actually points to the beginning
     // of the allocated memory.
@@ -305,7 +305,7 @@ class MallocInfo {
   }
 
   void PrintStatus() {
-    ScopedLock lock(&mu_);
+    BlockingMutexLock lock(&mu_);
     uptr malloced = 0;
 
     Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ",
@@ -323,7 +323,7 @@ class MallocInfo {
   }
 
   PageGroup *FindPageGroup(uptr addr) {
-    ScopedLock lock(&mu_);
+    BlockingMutexLock lock(&mu_);
     return FindPageGroupUnlocked(addr);
   }
 
@@ -481,7 +481,7 @@ class MallocInfo {
 
   AsanChunk *free_lists_[kNumberOfSizeClasses];
   AsanChunkFifoList quarantine_;
-  AsanLock mu_;
+  BlockingMutex mu_;
 
   PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize];
   atomic_uint32_t n_page_groups_;
index 6e90cb5f83f686cb6e78d0e22bbd864803500583..1cd9e517f322c0acef44f41d68fd8f26832c4b6a 100644 (file)
 //===----------------------------------------------------------------------===//
 #include "asan_interceptors.h"
 #include "asan_internal.h"
-#include "asan_lock.h"
 #include "asan_mapping.h"
 #include "asan_report.h"
 #include "asan_stack.h"
 #include "asan_stats.h"
 #include "asan_thread.h"
 #include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_mutex.h"
 
 namespace __asan {
 
@@ -30,7 +30,7 @@ struct ListOfGlobals {
   ListOfGlobals *next;
 };
 
-static AsanLock mu_for_globals(LINKER_INITIALIZED);
+static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
 static LowLevelAllocator allocator_for_globals;
 static ListOfGlobals *list_of_all_globals;
 static ListOfGlobals *list_of_dynamic_init_globals;
@@ -62,7 +62,7 @@ static uptr GetAlignedSize(uptr size) {
 
 bool DescribeAddressIfGlobal(uptr addr) {
   if (!flags()->report_globals) return false;
-  ScopedLock lock(&mu_for_globals);
+  BlockingMutexLock lock(&mu_for_globals);
   bool res = false;
   for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
     const Global &g = *l->g;
@@ -146,7 +146,7 @@ using namespace __asan;  // NOLINT
 void __asan_register_global(uptr addr, uptr size,
                             const char *name) {
   if (!flags()->report_globals) return;
-  ScopedLock lock(&mu_for_globals);
+  BlockingMutexLock lock(&mu_for_globals);
   Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
   g->beg = addr;
   g->size = size;
@@ -158,7 +158,7 @@ void __asan_register_global(uptr addr, uptr size,
 // Register an array of globals.
 void __asan_register_globals(__asan_global *globals, uptr n) {
   if (!flags()->report_globals) return;
-  ScopedLock lock(&mu_for_globals);
+  BlockingMutexLock lock(&mu_for_globals);
   for (uptr i = 0; i < n; i++) {
     RegisterGlobal(&globals[i]);
   }
@@ -168,7 +168,7 @@ void __asan_register_globals(__asan_global *globals, uptr n) {
 // We must do this when a shared objects gets dlclosed.
 void __asan_unregister_globals(__asan_global *globals, uptr n) {
   if (!flags()->report_globals) return;
-  ScopedLock lock(&mu_for_globals);
+  BlockingMutexLock lock(&mu_for_globals);
   for (uptr i = 0; i < n; i++) {
     UnregisterGlobal(&globals[i]);
   }
@@ -181,7 +181,7 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
 void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
   if (!flags()->check_initialization_order) return;
   CHECK(list_of_dynamic_init_globals);
-  ScopedLock lock(&mu_for_globals);
+  BlockingMutexLock lock(&mu_for_globals);
   bool from_current_tu = false;
   // The list looks like:
   // a => ... => b => last_addr => ... => first_addr => c => ...
@@ -202,7 +202,7 @@ void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
 // TU are poisoned.  It simply unpoisons all dynamically initialized globals.
 void __asan_after_dynamic_init() {
   if (!flags()->check_initialization_order) return;
-  ScopedLock lock(&mu_for_globals);
+  BlockingMutexLock lock(&mu_for_globals);
   for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
     UnpoisonGlobal(l->g);
 }
index ff91e8929a4cf6bd64f15921ca9e538eb6e63ce2..3f9370e304a8a17725ef63cc55bbd09df9dffacc 100644 (file)
@@ -15,7 +15,6 @@
 
 #include "asan_interceptors.h"
 #include "asan_internal.h"
-#include "asan_lock.h"
 #include "asan_thread.h"
 #include "asan_thread_registry.h"
 #include "sanitizer_common/sanitizer_libc.h"
@@ -102,26 +101,6 @@ void AsanPlatformThreadInit() {
   // Nothing here for now.
 }
 
-AsanLock::AsanLock(LinkerInitialized) {
-  // We assume that pthread_mutex_t initialized to all zeroes is a valid
-  // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
-  // a gcc warning:
-  // extended initializer lists only available with -std=c++0x or -std=gnu++0x
-}
-
-void AsanLock::Lock() {
-  CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_));
-  pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_);
-  CHECK(!owner_);
-  owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
-  CHECK(owner_ == (uptr)pthread_self());
-  owner_ = 0;
-  pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_);
-}
-
 void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
 #if defined(__arm__) || \
     defined(__powerpc__) || defined(__powerpc64__) || \
index edee49adf6a7e523bca319f381c9d6d9b0af8548..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,42 +0,0 @@
-//===-- asan_lock.h ---------------------------------------------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// A wrapper for a simple lock.
-//===----------------------------------------------------------------------===//
-#ifndef ASAN_LOCK_H
-#define ASAN_LOCK_H
-
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "asan_internal.h"
-
-// The locks in ASan are global objects and they are never destroyed to avoid
-// at-exit races (that is, a lock is being used by other threads while the main
-// thread is doing atexit destructors).
-// We define the class using opaque storage to avoid including system headers.
-
-namespace __asan {
-
-class AsanLock {
- public:
-  explicit AsanLock(LinkerInitialized);
-  void Lock();
-  void Unlock();
-  bool IsLocked() { return owner_ != 0; }
- private:
-  uptr opaque_storage_[10];
-  uptr owner_;  // for debugging and for malloc_introspection_t interface
-};
-
-typedef GenericScopedLock<AsanLock> ScopedLock;
-
-}  // namespace __asan
-
-#endif  // ASAN_LOCK_H
index 12d4f5b8ba6a6e26be5c36080e7ff8d7c96a7827..032861795e3e355544db92f07c44f695240ee204 100644 (file)
@@ -141,25 +141,6 @@ void AsanPlatformThreadInit() {
   }
 }
 
-AsanLock::AsanLock(LinkerInitialized) {
-  // We assume that OS_SPINLOCK_INIT is zero
-}
-
-void AsanLock::Lock() {
-  CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
-  CHECK(OS_SPINLOCK_INIT == 0);
-  CHECK(owner_ != (uptr)pthread_self());
-  OSSpinLockLock((OSSpinLock*)&opaque_storage_);
-  CHECK(!owner_);
-  owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
-  CHECK(owner_ == (uptr)pthread_self());
-  owner_ = 0;
-  OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
-}
-
 void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
   (void)fast;
   stack->size = 0;
index 9dadd6c02a3d81f05748a6e547b1d9d24a4f43d8..b910cf9aa3774629877c776df5b2661e901e0392 100644 (file)
@@ -14,7 +14,6 @@
 #include "asan_allocator.h"
 #include "asan_interceptors.h"
 #include "asan_internal.h"
-#include "asan_lock.h"
 #include "asan_mapping.h"
 #include "asan_report.h"
 #include "asan_stack.h"
index 86b6b4da6d6461ea97478705731cedcf0f1b3e21..c57c8cc61aed91deba38f9d3259c0d7203d54c39 100644 (file)
@@ -13,7 +13,6 @@
 //===----------------------------------------------------------------------===//
 #include "asan_interceptors.h"
 #include "asan_internal.h"
-#include "asan_lock.h"
 #include "asan_stats.h"
 #include "asan_thread_registry.h"
 #include "sanitizer/asan_interface.h"
@@ -55,13 +54,13 @@ void AsanStats::Print() {
              malloc_large, malloc_small_slow);
 }
 
-static AsanLock print_lock(LINKER_INITIALIZED);
+static BlockingMutex print_lock(LINKER_INITIALIZED);
 
 static void PrintAccumulatedStats() {
   AsanStats stats;
   asanThreadRegistry().GetAccumulatedStats(&stats);
   // Use lock to keep reports from mixing up.
-  ScopedLock lock(&print_lock);
+  BlockingMutexLock lock(&print_lock);
   stats.Print();
   StackDepotStats *stack_depot_stats = StackDepotGetStats();
   Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
index bcfe4f20c5afcfcc926d25002195c776ec3aea5a..80675405fbd576ea3a589d60695dc1174710eaf8 100644 (file)
@@ -44,7 +44,7 @@ void AsanThreadRegistry::Init() {
 }
 
 void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   u32 tid = n_threads_;
   n_threads_++;
   CHECK(n_threads_ < kMaxNumberOfThreads);
@@ -56,7 +56,7 @@ void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
 }
 
 void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   FlushToAccumulatedStatsUnlocked(&thread->stats());
   AsanThreadSummary *summary = thread->summary();
   CHECK(summary);
@@ -105,13 +105,13 @@ AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
 }
 
 void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
   internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_));
 }
 
 uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
   uptr malloced = accumulated_stats_.malloced;
   uptr freed = accumulated_stats_.freed;
@@ -121,13 +121,13 @@ uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
 }
 
 uptr AsanThreadRegistry::GetHeapSize() {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
   return accumulated_stats_.mmaped - accumulated_stats_.munmaped;
 }
 
 uptr AsanThreadRegistry::GetFreeBytes() {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
   uptr total_free = accumulated_stats_.mmaped
                   - accumulated_stats_.munmaped
@@ -143,7 +143,7 @@ uptr AsanThreadRegistry::GetFreeBytes() {
 // Return several stats counters with a single call to
 // UpdateAccumulatedStatsUnlocked().
 void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   UpdateAccumulatedStatsUnlocked();
   malloc_stats->blocks_in_use = accumulated_stats_.mallocs;
   malloc_stats->size_in_use = accumulated_stats_.malloced;
@@ -158,7 +158,7 @@ AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
 }
 
 AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
-  ScopedLock lock(&mu_);
+  BlockingMutexLock lock(&mu_);
   for (u32 tid = 0; tid < n_threads_; tid++) {
     AsanThread *t = thread_summaries_[tid]->thread();
     if (!t || !(t->fake_stack().StackSize())) continue;
index 2056e73fd302ab0eed5a75bd1129cb6bd3982267..adb1a6d4f32df0fdbba50bd2ebd70aad96502a48 100644 (file)
 #ifndef ASAN_THREAD_REGISTRY_H
 #define ASAN_THREAD_REGISTRY_H
 
-#include "asan_lock.h"
 #include "asan_stack.h"
 #include "asan_stats.h"
 #include "asan_thread.h"
+#include "sanitizer_common/sanitizer_mutex.h"
 
 namespace __asan {
 
@@ -73,7 +73,7 @@ class AsanThreadRegistry {
   // per-thread AsanStats.
   uptr max_malloced_memory_;
   u32 n_threads_;
-  AsanLock mu_;
+  BlockingMutex mu_;
   bool inited_;
 };
 
index daff20f0b5e56f860f15fddfcb3b77beee35ada0..86492daa7cbbedc813c1b61ad60931e705c816db 100644 (file)
 
 #include "asan_interceptors.h"
 #include "asan_internal.h"
-#include "asan_lock.h"
 #include "asan_thread.h"
 #include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_mutex.h"
 
 namespace __asan {
 
 // ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
-static AsanLock dbghelp_lock(LINKER_INITIALIZED);
+static BlockingMutex dbghelp_lock(LINKER_INITIALIZED);
 static bool dbghelp_initialized = false;
 #pragma comment(lib, "dbghelp.lib")
 
@@ -55,42 +54,6 @@ void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
     stack->trace[i] = (uptr)tmp[i + offset];
 }
 
-// ---------------------- AsanLock ---------------- {{{1
-enum LockState {
-  LOCK_UNINITIALIZED = 0,
-  LOCK_READY = -1,
-};
-
-AsanLock::AsanLock(LinkerInitialized li) {
-  // FIXME: see comments in AsanLock::Lock() for the details.
-  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
-
-  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
-  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
-  owner_ = LOCK_READY;
-}
-
-void AsanLock::Lock() {
-  if (owner_ == LOCK_UNINITIALIZED) {
-    // FIXME: hm, global AsanLock objects are not initialized?!?
-    // This might be a side effect of the clang+cl+link Frankenbuild...
-    new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
-
-    // FIXME: If it turns out the linker doesn't invoke our
-    // constructors, we should probably manually Lock/Unlock all the global
-    // locks while we're starting in one thread to avoid double-init races.
-  }
-  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
-  CHECK(owner_ == LOCK_READY);
-  owner_ = GetThreadSelf();
-}
-
-void AsanLock::Unlock() {
-  CHECK(owner_ == GetThreadSelf());
-  owner_ = LOCK_READY;
-  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
-}
-
 // ---------------------- TSD ---------------- {{{1
 static bool tsd_key_inited = false;
 
@@ -151,7 +114,7 @@ using namespace __asan;  // NOLINT
 extern "C" {
 SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
 bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) {
-  ScopedLock lock(&dbghelp_lock);
+  BlockingMutexLock lock(&dbghelp_lock);
   if (!dbghelp_initialized) {
     SymSetOptions(SYMOPT_DEFERRED_LOADS |
                   SYMOPT_UNDNAME |
index 98cf13d81961796114e02ceac1118e44319ef5cb..c2a58ee76945c925bb7a3994652beb9808a7a60f 100644 (file)
@@ -234,7 +234,7 @@ class SizeClassAllocator64 {
   Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) {
     CHECK_LT(class_id, kNumClasses);
     RegionInfo *region = GetRegionInfo(class_id);
-    SpinMutexLock l(&region->mutex);
+    BlockingMutexLock l(&region->mutex);
     if (region->free_list.empty())
       PopulateFreeList(c, class_id, region);
     CHECK(!region->free_list.empty());
@@ -246,7 +246,7 @@ class SizeClassAllocator64 {
 
   void NOINLINE DeallocateBatch(uptr class_id, Batch *b) {
     RegionInfo *region = GetRegionInfo(class_id);
-    SpinMutexLock l(&region->mutex);
+    BlockingMutexLock l(&region->mutex);
     region->free_list.push_front(b);
     region->n_freed++;
   }
@@ -343,7 +343,7 @@ class SizeClassAllocator64 {
   static const uptr kMetaMapSize = 1 << 16;
 
   struct RegionInfo {
-    SpinMutex mutex;
+    BlockingMutex mutex;
     IntrusiveList<Batch> free_list;
     uptr allocated_user;  // Bytes allocated for user memory.
     uptr allocated_meta;  // Bytes allocated for metadata.
index 614c5f7c7ba9a8e777e3d7f65d8339fa9ccbc1ec..f610d15b5a46f70f803e20d69244aeb58e77e4ab 100644 (file)
@@ -34,6 +34,7 @@
 #include <unwind.h>
 #include <errno.h>
 #include <sys/prctl.h>
+#include <linux/futex.h>
 
 // Are we using 32-bit or 64-bit syscalls?
 // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
@@ -436,6 +437,34 @@ void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
 
 #endif  // #ifndef SANITIZER_GO
 
+enum MutexState {
+  MtxUnlocked = 0,
+  MtxLocked = 1,
+  MtxSleeping = 2
+};
+
+BlockingMutex::BlockingMutex(LinkerInitialized) {
+}
+
+void BlockingMutex::Lock() {
+  atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+  if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+    return;
+  while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked)
+    syscall(__NR_futex, m, FUTEX_WAIT_PRIVATE, MtxSleeping, 0, 0, 0);
+}
+
+void BlockingMutex::Unlock() {
+  atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+  u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed);
+  if (v == MtxUnlocked) {
+    Printf("FATAL: unlock of unlocked mutex\n");
+    Die();
+  }
+  if (v == MtxSleeping)
+    syscall(__NR_futex, m, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0);
+}
+
 }  // namespace __sanitizer
 
 #endif  // __linux__
index e156eaa5221da69886af7c2227b64449f81c0111..52e184863cce0a61e9cf70d41e27a672d6a1ab48 100644 (file)
@@ -267,6 +267,25 @@ bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
   return IterateForObjectNameAndOffset(addr, offset, filename, filename_size);
 }
 
+BlockingMutex::BlockingMutex(LinkerInitialized) {
+  // We assume that OS_SPINLOCK_INIT is zero
+}
+
+void BlockingMutex::Lock() {
+  CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
+  CHECK(OS_SPINLOCK_INIT == 0);
+  CHECK(owner_ != (uptr)pthread_self());
+  OSSpinLockLock((OSSpinLock*)&opaque_storage_);
+  CHECK(!owner_);
+  owner_ = (uptr)pthread_self();
+}
+
+void BlockingMutex::Unlock() {
+  CHECK(owner_ == (uptr)pthread_self());
+  owner_ = 0;
+  OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+}
+
 }  // namespace __sanitizer
 
 #endif  // __APPLE__
index 4372a73c15ff51c2ca8dfe861faec0918e26e71d..15e1caa1b277d5a88f73377022710afaaff44b31 100644 (file)
@@ -67,6 +67,15 @@ class SpinMutex : public StaticSpinMutex {
   void operator=(const SpinMutex&);
 };
 
+class BlockingMutex {
+ public:
+  explicit BlockingMutex(LinkerInitialized);
+  void Lock();
+  void Unlock();
+ private:
+  uptr opaque_storage_[10];
+};
+
 template<typename MutexType>
 class GenericScopedLock {
  public:
@@ -106,6 +115,7 @@ class GenericScopedReadLock {
 };
 
 typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
+typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
 
 }  // namespace __sanitizer
 
index d84856832b6c5cea1f44f990545cdd32725635dc..a64c35d3038fbc991a739608946f82b346842a6a 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "sanitizer_common.h"
 #include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
 
 namespace __sanitizer {
 
@@ -226,6 +227,42 @@ int internal_sched_yield() {
   return 0;
 }
 
+// ---------------------- BlockingMutex ---------------- {{{1
+enum LockState {
+  LOCK_UNINITIALIZED = 0,
+  LOCK_READY = -1,
+};
+
+BlockingMutex::BlockingMutex(LinkerInitialized li) {
+  // FIXME: see comments in BlockingMutex::Lock() for the details.
+  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
+
+  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
+  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+  owner_ = LOCK_READY;
+}
+
+void BlockingMutex::Lock() {
+  if (owner_ == LOCK_UNINITIALIZED) {
+    // FIXME: hm, global BlockingMutex objects are not initialized?!?
+    // This might be a side effect of the clang+cl+link Frankenbuild...
+    new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));
+
+    // FIXME: If it turns out the linker doesn't invoke our
+    // constructors, we should probably manually Lock/Unlock all the global
+    // locks while we're starting in one thread to avoid double-init races.
+  }
+  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+  CHECK(owner_ == LOCK_READY);
+  owner_ = GetThreadSelf();
+}
+
+void BlockingMutex::Unlock() {
+  CHECK(owner_ == GetThreadSelf());
+  owner_ = LOCK_READY;
+  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+}
+
 }  // namespace __sanitizer
 
 #endif  // _WIN32
index 0cedeb58231d8ceaa6ed2c611185abb73e8cb6c6..50cc34cb56bd55048997183c286ca3e84cb5d999 100644 (file)
@@ -7,6 +7,7 @@ set(SANITIZER_UNITTESTS
   sanitizer_flags_test.cc
   sanitizer_libc_test.cc
   sanitizer_list_test.cc
+  sanitizer_mutex_test.cc
   sanitizer_printf_test.cc
   sanitizer_stackdepot_test.cc
   sanitizer_test_main.cc
index cf5b5fcbe59b31664be9d48b5673860e14c64106..593482fbb5da4086b91256a4715d08da42241650 100644 (file)
@@ -1,6 +1,6 @@
 DEBUG=0
 LDFLAGS=-ldl -lpthread -pie
-CXXFLAGS = -fPIE -g -Wall -Werror -DTSAN_DEBUG=$(DEBUG)
+CXXFLAGS = -fPIE -g -Wall -Werror -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG)
 # Silence warnings that Clang produces for gtest code.
 # Use -Wno-attributes so that gcc doesn't complain about unknown warning types.
 CXXFLAGS += -Wno-attributes
index 51da5bbfccf8a559796d362adb4cdb1ee05c8d28..f522ec6b47d7a94877e3ceab4cd00468fe084d9c 100644 (file)
@@ -1,4 +1,4 @@
-CXXFLAGS = -fPIE -g -Wall -Werror -fno-builtin -DTSAN_DEBUG=$(DEBUG)
+CXXFLAGS = -fPIE -g -Wall -Werror -fno-builtin -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG)
 ifeq ($(DEBUG), 0)
   CXXFLAGS += -O3
 endif