Detect over-long thread suspends and time out fatally.

Bug: 5726434
Change-Id: Ib7d4429c2b195f59133bb6dc7f9072b705c53e82
diff --git a/src/runtime.cc b/src/runtime.cc
index 6491138..b41ebde 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -639,6 +639,19 @@
   thread_list_->Dump(os);
 }
 
+void Runtime::DumpLockHolders(std::ostream& os) {
+  pid_t heap_lock_owner = Heap::GetLockOwner();
+  pid_t thread_list_lock_owner = GetThreadList()->GetLockOwner();
+  pid_t classes_lock_owner = GetClassLinker()->GetClassesLockOwner();
+  pid_t dex_lock_owner = GetClassLinker()->GetDexLockOwner();
+  if ((heap_lock_owner | thread_list_lock_owner | classes_lock_owner | dex_lock_owner) != 0) {
+    os << "Heap lock owner tid: " << heap_lock_owner << "\n"
+       << "ThreadList lock owner tid: " << thread_list_lock_owner << "\n"
+       << "ClassLinker classes lock owner tid: " << classes_lock_owner << "\n"
+       << "ClassLinker dex lock owner tid: " << dex_lock_owner << "\n";
+  }
+}
+
 void Runtime::SetStatsEnabled(bool new_state) {
   if (new_state == true) {
     GetStats()->Clear(~0);
diff --git a/src/runtime.h b/src/runtime.h
index 1d9a3d1..80534c2 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -119,6 +119,7 @@
   void DetachCurrentThread();
 
   void Dump(std::ostream& os);
+  void DumpLockHolders(std::ostream& os);
 
   ~Runtime();
 
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index d0cfe95..9031c5e 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -148,18 +148,8 @@
     // actually do what they want us to do...
     LOG(INFO) << *thread_ << ": reacting to signal " << signal_number;
 
-    // If anyone's holding locks (and so we might fail to get back into state Runnable), say so...
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    pid_t heap_lock_owner = Heap::GetLockOwner();
-    pid_t thread_list_lock_owner = Runtime::Current()->GetThreadList()->GetLockOwner();
-    pid_t classes_lock_owner = class_linker->GetClassesLockOwner();
-    pid_t dex_lock_owner = class_linker->GetDexLockOwner();
-    if ((heap_lock_owner | thread_list_lock_owner | classes_lock_owner | dex_lock_owner) != 0) {
-      LOG(INFO) << "Heap lock owner tid: " << heap_lock_owner << "\n"
-                << "ThreadList lock owner tid: " << thread_list_lock_owner << "\n"
-                << "ClassLinker classes lock owner tid: " << classes_lock_owner << "\n"
-                << "ClassLinker dex lock owner tid: " << dex_lock_owner << "\n";
-    }
+    // If anyone's holding locks (which might prevent us from getting back into state Runnable), say so...
+    Runtime::Current()->DumpLockHolders(LOG(INFO));
   }
 
   return signal_number;
diff --git a/src/thread.cc b/src/thread.cc
index 26eaf68..b828e94 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -644,10 +644,14 @@
 }
 
 void Thread::WaitUntilSuspended() {
-  // TODO: dalvik dropped the waiting thread's priority after a while.
-  // TODO: dalvik timed out and aborted.
+  static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
+
+  useconds_t total_delay = 0;
   useconds_t delay = 0;
   while (GetState() == Thread::kRunnable) {
+    if (total_delay >= kTimeoutUs) {
+      Runtime::Current()->DumpLockHolders(LOG(FATAL) << "Thread suspend timeout: " << *this << "\n");
+    }
     useconds_t new_delay = delay * 2;
     CHECK_GE(new_delay, delay);
     delay = new_delay;
@@ -656,6 +660,7 @@
       delay = 10000;
     } else {
       usleep(delay);
+      total_delay += delay;
     }
   }
 }