<div dir="ltr">1) Please add some tests for <span style="font-family:arial,sans-serif;font-size:13px">AddrHashMap and </span><span style="font-family:arial,sans-serif;font-size:13px">RWMutex to sanitizer_common/tests</span><div>
<span style="font-family:arial,sans-serif;font-size:13px">2) Add sanitizer_addrhashmap.h to lib/sanitizer_common/CMakeLists.txt</span></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Tue, Mar 4, 2014 at 3:39 PM, Dmitry Vyukov <span dir="ltr"><<a href="mailto:dvyukov@google.com" target="_blank">dvyukov@google.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: dvyukov<br>
Date: Tue Mar  4 05:39:56 2014<br>
New Revision: 202826<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=202826&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=202826&view=rev</a><br>
Log:<br>
tsan: add concurrent hashmap for standalone deadlock detector<br>
<br>
<br>
Added:<br>
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_addrhashmap.h<br>
    compiler-rt/trunk/lib/tsan/dd/<br>
      - copied from r202792, compiler-rt/trunk/lib/tsan/dd/<br>
Modified:<br>
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_mutex.h<br>
    compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc<br>
    compiler-rt/trunk/lib/tsan/dd/dd_rtl.h<br>
<br>
Added: compiler-rt/trunk/lib/sanitizer_common/sanitizer_addrhashmap.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_addrhashmap.h?rev=202826&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_addrhashmap.h?rev=202826&view=auto</a><br>

==============================================================================<br>
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_addrhashmap.h (added)<br>
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_addrhashmap.h Tue Mar  4 05:39:56 2014<br>
@@ -0,0 +1,207 @@<br>
+//===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// Concurrent uptr->T hashmap.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef SANITIZER_ADDRHASHMAP_H<br>
+#define SANITIZER_ADDRHASHMAP_H<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_mutex.h"<br>
+#include "sanitizer_atomic.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+// Concurrent uptr->T hashmap.<br>
+// T must be a POD type, kSize is preferrably a prime but can be any number.<br>
+// The hashmap is fixed size, it crashes on overflow.<br>
+// Usage example:<br>
+//<br>
+// typedef AddrHashMap<uptr, 11> Map;<br>
+// Map m;<br>
+// {<br>
+//   Map::Handle h(&m, addr);<br>
+//   use h.operator->() to access the data<br>
+//   if h.created() then the element was just created, and the current thread<br>
+//     has exclusive access to it<br>
+//   otherwise the current thread has only read access to the data<br>
+// }<br>
+// {<br>
+//   Map::Handle h(&m, addr, true);<br>
+//   this will remove the data from the map in Handle dtor<br>
+//   the current thread has exclusive access to the data<br>
+//   if !h.exists() then the element never existed<br>
+// }<br>
+template<typename T, uptr kSize><br>
+class AddrHashMap {<br>
+ private:<br>
+  struct Cell {<br>
+    RWMutex          mtx;<br>
+    atomic_uintptr_t addr;<br>
+    T                val;<br>
+  };<br>
+<br>
+ public:<br>
+  AddrHashMap();<br>
+<br>
+  class Handle {<br>
+   public:<br>
+    Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove = false);<br>
+    ~Handle();<br>
+    T *operator -> ();<br>
+    bool created() const;<br>
+    bool exists() const;<br>
+<br>
+   private:<br>
+    AddrHashMap<T, kSize> *map_;<br>
+    Cell                  *cell_;<br>
+    uptr                   addr_;<br>
+    bool                   created_;<br>
+    bool                   remove_;<br>
+    bool                   write_;<br>
+  };<br>
+<br>
+ private:<br>
+  friend class Handle;<br>
+  Cell *table_;<br>
+<br>
+  static const uptr kRemoved = 1;<br>
+<br>
+  Cell *acquire(uptr addr, bool remove, bool *created, bool *write);<br>
+  void  release(uptr addr, bool remove, bool write, Cell *c);<br>
+  uptr hash(uptr addr);<br>
+};<br>
+<br>
+template<typename T, uptr kSize><br>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,<br>
+    bool remove) {<br>
+  map_ = map;<br>
+  addr_ = addr;<br>
+  remove_ = remove;<br>
+  cell_ = map_->acquire(addr_, remove_, &created_, &write_);<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+AddrHashMap<T, kSize>::Handle::~Handle() {<br>
+  map_->release(addr_, remove_, write_, cell_);<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+T *AddrHashMap<T, kSize>::Handle::operator -> () {<br>
+  return &cell_->val;<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+bool AddrHashMap<T, kSize>::Handle::created() const {<br>
+  return created_;<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+bool AddrHashMap<T, kSize>::Handle::exists() const {<br>
+  return cell_ != 0;<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+AddrHashMap<T, kSize>::AddrHashMap() {<br>
+  table_ = (Cell*)MmapOrDie(kSize * sizeof(Cell), "AddrHashMap");<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+typename AddrHashMap<T, kSize>::Cell *AddrHashMap<T, kSize>::acquire(uptr addr,<br>
+    bool remove, bool *created, bool *write) {<br>
+  // When we access the element associated with addr,<br>
+  // we lock its home cell (the cell associated with hash(addr).<br>
+  // If the element was just created or is going to be removed,<br>
+  // we lock the cell in write mode. Otherwise we lock in read mode.<br>
+  // The locking protects the object lifetime (it's not removed while<br>
+  // somebody else accesses it). And also it helps to resolve concurrent<br>
+  // inserts.<br>
+  // Note that the home cell is not necessary the cell where the element is<br>
+  // stored.<br>
+  *write = false;<br>
+  *created = false;<br>
+  uptr h0 = hash(addr);<br>
+  Cell *c0 = &table_[h0];<br>
+  // First try to find an existing element under read lock.<br>
+  if (!remove) {<br>
+    c0->mtx.ReadLock();<br>
+    uptr h = h0;<br>
+    for (;;) {<br>
+      Cell *c = &table_[h];<br>
+      uptr addr1 = atomic_load(&c->addr, memory_order_acquire);<br>
+      if (addr1 == 0)  // empty cell denotes end of the cell chain for the elem<br>
+        break;<br>
+      if (addr1 == addr)  // ok, found it<br>
+        return c;<br>
+      h++;<br>
+      if (h == kSize)<br>
+        h = 0;<br>
+      CHECK_NE(h, h0);  // made the full cycle<br>
+    }<br>
+    c0->mtx.ReadUnlock();<br>
+  }<br>
+  // Now try to create it under write lock.<br>
+  *write = true;<br>
+  c0->mtx.Lock();<br>
+  uptr h = h0;<br>
+  for (;;) {<br>
+    Cell *c = &table_[h];<br>
+    uptr addr1 = atomic_load(&c->addr, memory_order_acquire);<br>
+    if (addr1 == addr)  // another thread has inserted it ahead of us<br>
+      return c;<br>
+    if (remove && addr1 == 0) {  // the element has not existed before<br>
+      c0->mtx.Unlock();<br>
+      return 0;<br>
+    }<br>
+    if (!remove && (addr1 == 0 ||addr1 == kRemoved) &&<br>
+        atomic_compare_exchange_strong(&c->addr, &addr1, addr,<br>
+          memory_order_acq_rel)) {<br>
+      // we've created the element<br>
+      *created = true;<br>
+      return c;<br>
+    }<br>
+    h++;<br>
+    if (h == kSize)<br>
+      h = 0;<br>
+    CHECK_NE(h, h0);  // made the full cycle<br>
+  }<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+void AddrHashMap<T, kSize>::release(uptr addr, bool remove, bool write,<br>
+    Cell *c) {<br>
+  if (c == 0)<br>
+    return;<br>
+  // if we are going to remove, we must hold write lock<br>
+  CHECK_EQ(!remove || write, true);<br>
+  CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), addr);<br>
+  // denote that the cell is empty now<br>
+  if (remove)<br>
+    atomic_store(&c->addr, kRemoved, memory_order_release);<br>
+  // unlock the home cell<br>
+  uptr h0 = hash(addr);<br>
+  Cell *c0 = &table_[h0];<br>
+  if (write)<br>
+    c0->mtx.Unlock();<br>
+  else<br>
+    c0->mtx.ReadUnlock();<br>
+}<br>
+<br>
+template<typename T, uptr kSize><br>
+uptr AddrHashMap<T, kSize>::hash(uptr addr) {<br>
+  addr += addr << 10;<br>
+  addr ^= addr >> 6;<br>
+  return addr % kSize;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_ADDRHASHMAP_H<br>
<br>
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_mutex.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_mutex.h?rev=202826&r1=202825&r2=202826&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_mutex.h?rev=202826&r1=202825&r2=202826&view=diff</a><br>

==============================================================================<br>
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_mutex.h (original)<br>
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_mutex.h Tue Mar  4 05:39:56 2014<br>
@@ -83,6 +83,88 @@ class BlockingMutex {<br>
   uptr owner_;  // for debugging<br>
 };<br>
<br>
+// Reader-writer spin mutex.<br>
+class RWMutex {<br>
+ public:<br>
+  RWMutex() {<br>
+    atomic_store(&state_, kUnlocked, memory_order_relaxed);<br>
+  }<br>
+<br>
+  ~RWMutex() {<br>
+    CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);<br>
+  }<br>
+<br>
+  void Lock() {<br>
+    uptr cmp = kUnlocked;<br>
+    if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,<br>
+                                       memory_order_acquire))<br>
+      return;<br>
+    LockSlow();<br>
+  }<br>
+<br>
+  void Unlock() {<br>
+    uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);<br>
+    DCHECK_NE(prev & kWriteLock, 0);<br>
+    (void)prev;<br>
+  }<br>
+<br>
+  void ReadLock() {<br>
+    uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);<br>
+    if ((prev & kWriteLock) == 0)<br>
+      return;<br>
+    ReadLockSlow();<br>
+  }<br>
+<br>
+  void ReadUnlock() {<br>
+    uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);<br>
+    DCHECK_EQ(prev & kWriteLock, 0);<br>
+    DCHECK_GT(prev & ~kWriteLock, 0);<br>
+    (void)prev;<br>
+  }<br>
+<br>
+  void CheckLocked() {<br>
+    CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);<br>
+  }<br>
+<br>
+ private:<br>
+  atomic_uintptr_t state_;<br>
+<br>
+  enum {<br>
+    kUnlocked = 0,<br>
+    kWriteLock = 1,<br>
+    kReadLock = 2<br>
+  };<br>
+<br>
+  void NOINLINE LockSlow() {<br>
+    for (int i = 0;; i++) {<br>
+      if (i < 10)<br>
+        proc_yield(10);<br>
+      else<br>
+        internal_sched_yield();<br>
+      uptr cmp = atomic_load(&state_, memory_order_relaxed);<br>
+      if (cmp == kUnlocked &&<br>
+          atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,<br>
+                                       memory_order_acquire))<br>
+          return;<br>
+    }<br>
+  }<br>
+<br>
+  void NOINLINE ReadLockSlow() {<br>
+    for (int i = 0;; i++) {<br>
+      if (i < 10)<br>
+        proc_yield(10);<br>
+      else<br>
+        internal_sched_yield();<br>
+      uptr  prev = atomic_load(&state_, memory_order_acquire);<br>
+      if ((prev & kWriteLock) == 0)<br>
+        return;<br>
+    }<br>
+  }<br>
+<br>
+  RWMutex(const RWMutex&);<br>
+  void operator = (const RWMutex&);<br>
+};<br>
+<br>
 template<typename MutexType><br>
 class GenericScopedLock {<br>
  public:<br>
@@ -123,6 +205,8 @@ class GenericScopedReadLock {<br>
<br>
 typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;<br>
 typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;<br>
+typedef GenericScopedLock<RWMutex> RWMutexLock;<br>
+typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;<br>
<br>
 }  // namespace __sanitizer<br>
<br>
<br>
Modified: compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc?rev=202826&r1=202792&r2=202826&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc?rev=202826&r1=202792&r2=202826&view=diff</a><br>

==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc (original)<br>
+++ compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc Tue Mar  4 05:39:56 2014<br>
@@ -9,16 +9,19 @@<br>
<br>
 #include "dd_rtl.h"<br>
 #include "sanitizer_common/sanitizer_common.h"<br>
+#include "sanitizer_common/sanitizer_placement_new.h"<br>
 #include "sanitizer_common/sanitizer_flags.h"<br>
 #include "sanitizer_common/sanitizer_stacktrace.h"<br>
 #include "sanitizer_common/sanitizer_stackdepot.h"<br>
<br>
 namespace __dsan {<br>
<br>
-static Context ctx0;<br>
-static Context * const ctx = &ctx0;<br>
+static Context *ctx;<br>
<br>
 void Initialize() {<br>
+  static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];<br>
+  ctx = new(ctx_mem) Context();<br>
+<br>
   InitializeInterceptors();<br>
   //common_flags()->allow_addr2line = true;<br>
   common_flags()->symbolize = true;<br>
@@ -37,9 +40,9 @@ void ThreadDestroy(Thread *thr) {<br>
<br>
 static u32 CurrentStackTrace(Thread *thr) {<br>
   StackTrace trace;<br>
-  thr->in_symbolizer = true;<br>
+  thr->ignore_interceptors = true;<br>
   trace.Unwind(1000, 0, 0, 0, 0, 0, false);<br>
-  thr->in_symbolizer = false;<br>
+  thr->ignore_interceptors = false;<br>
   const uptr skip = 4;<br>
   if (trace.size <= skip)<br>
     return 0;<br>
@@ -49,39 +52,9 @@ static u32 CurrentStackTrace(Thread *thr<br>
 static void PrintStackTrace(Thread *thr, u32 stk) {<br>
   uptr size = 0;<br>
   const uptr *trace = StackDepotGet(stk, &size);<br>
-  thr->in_symbolizer = true;<br>
+  thr->ignore_interceptors = true;<br>
   StackTrace::PrintStack(trace, size);<br>
-  thr->in_symbolizer = false;<br>
-}<br>
-<br>
-static Mutex *FindMutex(Thread *thr, uptr m) {<br>
-  SpinMutexLock l(&ctx->mutex_mtx);<br>
-  for (Mutex *mtx = ctx->mutex_list; mtx; mtx = mtx->link) {<br>
-    if (mtx->addr == m)<br>
-      return mtx;<br>
-  }<br>
-  Mutex *mtx = (Mutex*)InternalAlloc(sizeof(*mtx));<br>
-  internal_memset(mtx, 0, sizeof(*mtx));<br>
-  mtx->addr = m;<br>
-  ctx->dd->MutexInit(&mtx->dd, CurrentStackTrace(thr), ctx->mutex_seq++);<br>
-  mtx->link = ctx->mutex_list;<br>
-  ctx->mutex_list = mtx;<br>
-  return mtx;<br>
-}<br>
-<br>
-static Mutex *FindMutexAndRemove(uptr m) {<br>
-  SpinMutexLock l(&ctx->mutex_mtx);<br>
-  Mutex **prev = &ctx->mutex_list;<br>
-  for (;;) {<br>
-    Mutex *mtx = *prev;<br>
-    if (mtx == 0)<br>
-      return 0;<br>
-    if (mtx->addr == m) {<br>
-      *prev = mtx->link;<br>
-      return mtx;<br>
-    }<br>
-    prev = &mtx->link;<br>
-  }<br>
+  thr->ignore_interceptors = false;<br>
 }<br>
<br>
 static void ReportDeadlock(Thread *thr, DDReport *rep) {<br>
@@ -96,30 +69,34 @@ static void ReportDeadlock(Thread *thr,<br>
 }<br>
<br>
 void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) {<br>
-  if (thr->in_symbolizer)<br>
+  if (thr->ignore_interceptors)<br>
     return;<br>
-  Mutex *mtx = FindMutex(thr, m);<br>
-  DDReport *rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &mtx->dd,<br>
-      writelock, trylock);<br>
+  DDReport *rep = 0;<br>
+  {<br>
+    MutexHashMap::Handle h(&ctx->mutex_map, m);<br>
+    if (h.created())<br>
+      ctx->dd->MutexInit(&h->dd, CurrentStackTrace(thr), m);<br>
+    rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &h->dd,<br>
+                             writelock, trylock);<br>
+  }<br>
   if (rep)<br>
     ReportDeadlock(thr, rep);<br>
 }<br>
<br>
 void MutexUnlock(Thread *thr, uptr m, bool writelock) {<br>
-  if (thr->in_symbolizer)<br>
+  if (thr->ignore_interceptors)<br>
     return;<br>
-  Mutex *mtx = FindMutex(thr, m);<br>
-  ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &mtx->dd, writelock);<br>
+  MutexHashMap::Handle h(&ctx->mutex_map, m);<br>
+  ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &h->dd, writelock);<br>
 }<br>
<br>
 void MutexDestroy(Thread *thr, uptr m) {<br>
-  if (thr->in_symbolizer)<br>
+  if (thr->ignore_interceptors)<br>
     return;<br>
-  Mutex *mtx = FindMutexAndRemove(m);<br>
-  if (mtx == 0)<br>
+  MutexHashMap::Handle h(&ctx->mutex_map, m, true);<br>
+  if (!h.exists())<br>
     return;<br>
-  ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &mtx->dd);<br>
-  InternalFree(mtx);<br>
+  ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &h->dd);<br>
 }<br>
<br>
 }  // namespace __dsan<br>
<br>
Modified: compiler-rt/trunk/lib/tsan/dd/dd_rtl.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.h?rev=202826&r1=202792&r2=202826&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.h?rev=202826&r1=202792&r2=202826&view=diff</a><br>

==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/dd_rtl.h (original)<br>
+++ compiler-rt/trunk/lib/tsan/dd/dd_rtl.h Tue Mar  4 05:39:56 2014<br>
@@ -12,13 +12,12 @@<br>
 #include "sanitizer_common/sanitizer_internal_defs.h"<br>
 #include "sanitizer_common/sanitizer_deadlock_detector_interface.h"<br>
 #include "sanitizer_common/sanitizer_allocator_internal.h"<br>
+#include "sanitizer_common/sanitizer_addrhashmap.h"<br>
 #include "sanitizer_common/sanitizer_mutex.h"<br>
<br>
 namespace __dsan {<br>
<br>
 struct Mutex {<br>
-  Mutex *link;<br>
-  uptr addr;<br>
   DDMutex dd;<br>
 };<br>
<br>
@@ -26,15 +25,15 @@ struct Thread {<br>
   DDPhysicalThread *dd_pt;<br>
   DDLogicalThread *dd_lt;<br>
<br>
-  bool in_symbolizer;<br>
+  bool ignore_interceptors;<br>
 };<br>
<br>
+typedef AddrHashMap<Mutex, 1000003> MutexHashMap;<br>
+<br>
 struct Context {<br>
   DDetector *dd;<br>
<br>
-  SpinMutex mutex_mtx;<br>
-  Mutex *mutex_list;<br>
-  u64 mutex_seq;<br>
+  MutexHashMap mutex_map;<br>
 };<br>
<br>
 void InitializeInterceptors();<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div>Alexey Samsonov, MSK</div>
</div>