Merge "Fix a deadlock caused by my big threading change yesterday." into dalvik-dev
diff --git a/src/mutex.cc b/src/mutex.cc
index 963ac99..f7c3143 100644
--- a/src/mutex.cc
+++ b/src/mutex.cc
@@ -27,14 +27,23 @@
 
 namespace art {
 
-static inline void CheckRank(MutexRank rank, bool is_locking) {
+static inline void CheckSafeToLockOrUnlock(MutexRank rank, bool is_locking) {
 #ifndef NDEBUG
   if (rank == -1) {
     return;
   }
   Thread* self = Thread::Current();
   if (self != NULL) {
-    self->CheckRank(rank, is_locking);
+    self->CheckSafeToLockOrUnlock(rank, is_locking);
+  }
+#endif
+}
+
+static inline void CheckSafeToWait(MutexRank rank) {
+#ifndef NDEBUG
+  Thread* self = Thread::Current();
+  if (self != NULL) {
+    self->CheckSafeToWait(rank);
   }
 #endif
 }
@@ -58,8 +67,8 @@
 }
 
 void Mutex::Lock() {
+  CheckSafeToLockOrUnlock(rank_, true);
   CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_));
-  CheckRank(rank_, true);
   AssertHeld();
 }
 
@@ -72,15 +81,15 @@
     errno = result;
     PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_;
   }
-  CheckRank(rank_, true);
+  CheckSafeToLockOrUnlock(rank_, true);
   AssertHeld();
   return true;
 }
 
 void Mutex::Unlock() {
   AssertHeld();
+  CheckSafeToLockOrUnlock(rank_, false);
   CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_));
-  CheckRank(rank_, false);
 }
 
 pid_t Mutex::GetOwner() {
@@ -151,6 +160,7 @@
 }
 
 void ConditionVariable::Wait(Mutex& mutex) {
+  CheckSafeToWait(mutex.GetRank());
   CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, mutex.GetImpl()));
 }
 
@@ -160,6 +170,7 @@
 #else
 #define TIMEDWAIT pthread_cond_timedwait
 #endif
+  CheckSafeToWait(mutex.GetRank());
   int rc = TIMEDWAIT(&cond_, mutex.GetImpl(), &ts);
   if (rc != 0 && rc != ETIMEDOUT) {
     errno = rc;
diff --git a/src/mutex.h b/src/mutex.h
index b4774d7..475c7dd 100644
--- a/src/mutex.h
+++ b/src/mutex.h
@@ -56,6 +56,10 @@
     return &mutex_;
   }
 
+  MutexRank GetRank() const {
+    return rank_;
+  }
+
   void AssertHeld() {
 #if !defined(__APPLE__)
     DCHECK_EQ(GetOwner(), GetTid());
diff --git a/src/thread.cc b/src/thread.cc
index 1c521c1..dcaf01f 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -599,6 +599,12 @@
   WalkStack(&dumper);
 }
 
+void Thread::SetStateWithoutSuspendCheck(Thread::State new_state) {
+  volatile void* raw = reinterpret_cast<volatile void*>(&state_);
+  volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+  android_atomic_release_store(new_state, addr);
+}
+
 Thread::State Thread::SetState(Thread::State new_state) {
   Thread::State old_state = state_;
   if (old_state == new_state) {
@@ -1674,7 +1680,7 @@
   return os;
 }
 
-void Thread::CheckRank(MutexRank rank, bool is_locking) {
+void Thread::CheckSafeToLockOrUnlock(MutexRank rank, bool is_locking) {
   if (is_locking) {
     if (held_mutexes_[rank] == 0) {
       bool bad_mutexes_held = false;
@@ -1693,4 +1699,19 @@
   }
 }
 
+void Thread::CheckSafeToWait(MutexRank rank) {
+  bool bad_mutexes_held = false;
+  for (int i = kMaxMutexRank; i >= 0; --i) {
+    if (i != rank && held_mutexes_[i] != 0) {
+      LOG(ERROR) << "holding " << static_cast<MutexRank>(i) << " while doing condition variable wait on " << rank;
+      bad_mutexes_held = true;
+    }
+  }
+  if (held_mutexes_[rank] == 0) {
+    LOG(ERROR) << "*not* holding " << rank << " while doing condition variable wait on it";
+    bad_mutexes_held = true;
+  }
+  CHECK(!bad_mutexes_held);
+}
+
 }  // namespace art
diff --git a/src/thread.h b/src/thread.h
index 02fa87c..7ad433c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -122,6 +122,7 @@
   }
 
   State SetState(State new_state);
+  void SetStateWithoutSuspendCheck(State new_state);
 
   bool IsDaemon();
   bool IsSuspended();
@@ -422,7 +423,8 @@
     return frame;
   }
 
-  void CheckRank(MutexRank rank, bool is_locking);
+  void CheckSafeToLockOrUnlock(MutexRank rank, bool is_locking);
+  void CheckSafeToWait(MutexRank rank);
 
  private:
   Thread();
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 3f8789a..e780e2c 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -33,8 +33,13 @@
       // Self may be null during shutdown, but in that case there's no point going to kVmWait.
       thread_list->thread_list_lock_.Lock();
     } else {
-      ScopedThreadStateChange tsc(self, Thread::kVmWait);
+      Thread::State old_thread_state = self->SetState(Thread::kVmWait);
       thread_list->thread_list_lock_.Lock();
+      // If we have the lock, by definition there's no GC in progress (though we
+      // might be taking the lock in order to start one). We avoid the suspend
+      // check here so we don't risk going to sleep on the thread suspend count lock
+      // while holding the thread list lock.
+      self->SetStateWithoutSuspendCheck(old_thread_state);
     }
   }
 }