tsan: introduce recursive mutex lock/unlock  java interface
this is required to handle Object.Wait()

llvm-svn: 182088
diff --git a/compiler-rt/lib/tsan/lit_tests/java.h b/compiler-rt/lib/tsan/lit_tests/java.h
index 7d61f58..0409419 100644
--- a/compiler-rt/lib/tsan/lit_tests/java.h
+++ b/compiler-rt/lib/tsan/lit_tests/java.h
@@ -14,4 +14,6 @@
 void __tsan_java_mutex_unlock(jptr addr);
 void __tsan_java_mutex_read_lock(jptr addr);
 void __tsan_java_mutex_read_unlock(jptr addr);
+void __tsan_java_mutex_lock_rec(jptr addr, int rec);
+int  __tsan_java_mutex_unlock_rec(jptr addr);
 }
diff --git a/compiler-rt/lib/tsan/lit_tests/java_lock_rec.cc b/compiler-rt/lib/tsan/lit_tests/java_lock_rec.cc
new file mode 100644
index 0000000..5cc80d4
--- /dev/null
+++ b/compiler-rt/lib/tsan/lit_tests/java_lock_rec.cc
@@ -0,0 +1,54 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <unistd.h>
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+  __tsan_java_mutex_lock(lockaddr);
+  __tsan_java_mutex_lock(lockaddr);
+  *(int*)varaddr = 42;
+  int rec = __tsan_java_mutex_unlock_rec(lockaddr);
+  if (rec != 2) {
+    printf("FAILED 0 rec=%d\n", rec);
+    exit(1);
+  }
+  sleep(2);
+  __tsan_java_mutex_lock_rec(lockaddr, rec);
+  if (*(int*)varaddr != 43) {
+    printf("FAILED 3 var=%d\n", *(int*)varaddr);
+    exit(1);
+  }
+  __tsan_java_mutex_unlock(lockaddr);
+  __tsan_java_mutex_unlock(lockaddr);
+  return 0;
+}
+
+int main() {
+  int const kHeapSize = 1024 * 1024;
+  void *jheap = malloc(kHeapSize);
+  __tsan_java_init((jptr)jheap, kHeapSize);
+  const int kBlockSize = 16;
+  __tsan_java_alloc((jptr)jheap, kBlockSize);
+  varaddr = (jptr)jheap;
+  *(int*)varaddr = 0;
+  lockaddr = (jptr)jheap + 8;
+  pthread_t th;
+  pthread_create(&th, 0, Thread, 0);
+  sleep(1);
+  __tsan_java_mutex_lock(lockaddr);
+  if (*(int*)varaddr != 42) {
+    printf("FAILED 1 var=%d\n", *(int*)varaddr);
+    exit(1);
+  }
+  *(int*)varaddr = 43;
+  __tsan_java_mutex_unlock(lockaddr);
+  pthread_join(th, 0);
+  __tsan_java_free((jptr)jheap, kBlockSize);
+  printf("OK\n");
+  return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
diff --git a/compiler-rt/lib/tsan/lit_tests/java_lock_rec_race.cc b/compiler-rt/lib/tsan/lit_tests/java_lock_rec_race.cc
new file mode 100644
index 0000000..61626aa
--- /dev/null
+++ b/compiler-rt/lib/tsan/lit_tests/java_lock_rec_race.cc
@@ -0,0 +1,48 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <unistd.h>
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+  __tsan_java_mutex_lock(lockaddr);
+  __tsan_java_mutex_lock(lockaddr);
+  __tsan_java_mutex_lock(lockaddr);
+  int rec = __tsan_java_mutex_unlock_rec(lockaddr);
+  if (rec != 3) {
+    printf("FAILED 0 rec=%d\n", rec);
+    exit(1);
+  }
+  *(int*)varaddr = 42;
+  sleep(2);
+  __tsan_java_mutex_lock_rec(lockaddr, rec);
+  __tsan_java_mutex_unlock(lockaddr);
+  __tsan_java_mutex_unlock(lockaddr);
+  __tsan_java_mutex_unlock(lockaddr);
+  return 0;
+}
+
+int main() {
+  int const kHeapSize = 1024 * 1024;
+  void *jheap = malloc(kHeapSize);
+  __tsan_java_init((jptr)jheap, kHeapSize);
+  const int kBlockSize = 16;
+  __tsan_java_alloc((jptr)jheap, kBlockSize);
+  varaddr = (jptr)jheap;
+  *(int*)varaddr = 0;
+  lockaddr = (jptr)jheap + 8;
+  pthread_t th;
+  pthread_create(&th, 0, Thread, 0);
+  sleep(1);
+  __tsan_java_mutex_lock(lockaddr);
+  *(int*)varaddr = 43;
+  __tsan_java_mutex_unlock(lockaddr);
+  pthread_join(th, 0);
+  __tsan_java_free((jptr)jheap, kBlockSize);
+  printf("OK\n");
+  return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc b/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc
index ee12001..71e0747 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc
@@ -271,6 +271,7 @@
   CHECK_GE(addr, jctx->heap_begin);
   CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
 
+  MutexCreate(thr, pc, addr, true, true, true);
   MutexLock(thr, pc, addr);
 }
 
@@ -291,6 +292,7 @@
   CHECK_GE(addr, jctx->heap_begin);
   CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
 
+  MutexCreate(thr, pc, addr, true, true, true);
   MutexReadLock(thr, pc, addr);
 }
 
@@ -303,3 +305,25 @@
 
   MutexReadUnlock(thr, pc, addr);
 }
+
+void __tsan_java_mutex_lock_rec(jptr addr, int rec) {
+  SCOPED_JAVA_FUNC(__tsan_java_mutex_lock_rec);
+  DPrintf("#%d: java_mutex_lock_rec(%p, %d)\n", thr->tid, addr, rec);
+  CHECK_NE(jctx, 0);
+  CHECK_GE(addr, jctx->heap_begin);
+  CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+  CHECK_GT(rec, 0);
+
+  MutexCreate(thr, pc, addr, true, true, true);
+  MutexLock(thr, pc, addr, rec);
+}
+
+int __tsan_java_mutex_unlock_rec(jptr addr) {
+  SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock_rec);
+  DPrintf("#%d: java_mutex_unlock_rec(%p)\n", thr->tid, addr);
+  CHECK_NE(jctx, 0);
+  CHECK_GE(addr, jctx->heap_begin);
+  CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+  return MutexUnlock(thr, pc, addr, true);
+}
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_java.h b/compiler-rt/lib/tsan/rtl/tsan_interface_java.h
index 241483a..9ac78e0 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_java.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_java.h
@@ -55,8 +55,7 @@
 
 // Mutex lock.
 // Addr is any unique address associated with the mutex.
-// Must not be called on recursive reentry.
-// Object.wait() is handled as a pair of unlock/lock.
+// Can be called on recursive reentry.
 void __tsan_java_mutex_lock(jptr addr) INTERFACE_ATTRIBUTE;
 // Mutex unlock.
 void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE;
@@ -64,6 +63,16 @@
 void __tsan_java_mutex_read_lock(jptr addr) INTERFACE_ATTRIBUTE;
 // Mutex read unlock.
 void __tsan_java_mutex_read_unlock(jptr addr) INTERFACE_ATTRIBUTE;
+// Recursive mutex lock, intended for handling of Object.wait().
+// The 'rec' value must be obtained from the previous
+// __tsan_java_mutex_unlock_rec().
+void __tsan_java_mutex_lock_rec(jptr addr, int rec) INTERFACE_ATTRIBUTE;
+// Recursive mutex unlock, intended for handling of Object.wait().
+// The return value says how many times this thread called lock()
+// w/o a pairing unlock() (i.e. how many recursive levels it unlocked).
+// It must be passed back to __tsan_java_mutex_lock_rec() to restore
+// the same recursion level.
+int __tsan_java_mutex_unlock_rec(jptr addr) INTERFACE_ATTRIBUTE;
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index efd6b14..f1a73e4 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -691,8 +691,8 @@
 void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
                  bool rw, bool recursive, bool linker_init);
 void MutexDestroy(ThreadState *thr, uptr pc, uptr addr);
-void MutexLock(ThreadState *thr, uptr pc, uptr addr);
-void MutexUnlock(ThreadState *thr, uptr pc, uptr addr);
+void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1);
+int  MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false);
 void MutexReadLock(ThreadState *thr, uptr pc, uptr addr);
 void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
 void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc
index 1f3c7ac..cf2e44d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc
@@ -79,9 +79,10 @@
   DestroyAndFree(s);
 }
 
-void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
+void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
   CHECK_GT(thr->in_rtl, 0);
-  DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
+  DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec);
+  CHECK_GT(rec, 0);
   if (IsAppMem(addr))
     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
@@ -107,19 +108,20 @@
   } else if (!s->is_recursive) {
     StatInc(thr, StatMutexRecLock);
   }
-  s->recursion++;
+  s->recursion += rec;
   thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
   s->mtx.Unlock();
 }
 
-void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
+int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) {
   CHECK_GT(thr->in_rtl, 0);
-  DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
+  DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all);
   if (IsAppMem(addr))
     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
   thr->fast_state.IncrementEpoch();
   TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
+  int rec = 0;
   if (s->recursion == 0) {
     if (!s->is_broken) {
       s->is_broken = true;
@@ -133,7 +135,8 @@
       PrintCurrentStack(thr, pc);
     }
   } else {
-    s->recursion--;
+    rec = all ? s->recursion : 1;
+    s->recursion -= rec;
     if (s->recursion == 0) {
       StatInc(thr, StatMutexUnlock);
       s->owner_tid = SyncVar::kInvalidTid;
@@ -147,6 +150,7 @@
   }
   thr->mset.Del(s->GetId(), true);
   s->mtx.Unlock();
+  return rec;
 }
 
 void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {