Add a way to SuspendAll for a long duration

Hprof uses this mode to not cause thread suspend timeouts if GC tries
while the hprof dump is running.

(cherry picked from commit 77be6635f9b07a8a794924c5fb9b071949776a6d)

Bug: 21063989
Change-Id: Ic6304620afd1489719a7e0e4299f829c90fe27cc
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 88a72ec..917fe43 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -1214,7 +1214,7 @@
     // comment in Heap::VisitObjects().
     heap->IncrementDisableMovingGC(self);
   }
-  Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__);
+  Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__, true /* long suspend */);
   Hprof hprof(filename, fd, direct_to_ddms);
   hprof.Dump();
   Runtime::Current()->GetThreadList()->ResumeAll();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 7719bb8..af9ba68 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -52,7 +52,7 @@
 
 ThreadList::ThreadList()
     : suspend_all_count_(0), debug_suspend_all_count_(0), unregistering_count_(0),
-      suspend_all_historam_("suspend all histogram", 16, 64) {
+      suspend_all_historam_("suspend all histogram", 16, 64), long_suspend_(false) {
   CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U)));
 }
 
@@ -448,7 +448,7 @@
   return runnable_threads.size() + other_threads.size() + 1;  // +1 for self.
 }
 
-void ThreadList::SuspendAll(const char* cause) {
+void ThreadList::SuspendAll(const char* cause, bool long_suspend) {
   Thread* self = Thread::Current();
 
   if (self != nullptr) {
@@ -482,14 +482,22 @@
 
   // Block on the mutator lock until all Runnable threads release their share of access.
 #if HAVE_TIMED_RWLOCK
-  // Timeout if we wait more than 30 seconds.
-  if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) {
-    UnsafeLogFatalForThreadSuspendAllTimeout();
+  while (true) {
+    if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) {
+      break;
+    } else if (!long_suspend_) {
+      // Reading long_suspend without the mutator lock is slightly racy, in some rare cases, this
+      // could result in a thread suspend timeout.
+      // Timeout if we wait more than kThreadSuspendTimeoutMs seconds.
+      UnsafeLogFatalForThreadSuspendAllTimeout();
+    }
   }
 #else
   Locks::mutator_lock_->ExclusiveLock(self);
 #endif
 
+  long_suspend_ = long_suspend;
+
   const uint64_t end_time = NanoTime();
   const uint64_t suspend_time = end_time - start_time;
   suspend_all_historam_.AdjustAndAddValue(suspend_time);
@@ -529,6 +537,8 @@
     AssertThreadsAreSuspended(self, self);
   }
 
+  long_suspend_ = false;
+
   Locks::mutator_lock_->ExclusiveUnlock(self);
   {
     MutexLock mu(self, *Locks::thread_list_lock_);
@@ -599,8 +609,8 @@
                                        jobject peer) {
   JNIEnvExt* env = self->GetJniEnv();
   ScopedLocalRef<jstring>
-      scoped_name_string(env, (jstring)env->GetObjectField(peer,
-                                                          WellKnownClasses::java_lang_Thread_name));
+      scoped_name_string(env, (jstring)env->GetObjectField(
+          peer, WellKnownClasses::java_lang_Thread_name));
   ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
   if (scoped_name_chars.c_str() == nullptr) {
       LOG(severity) << message << ": " << peer;
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 0f094cc..2c1f813 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -61,7 +61,9 @@
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_);
 
   // Suspends all threads and gets exclusive access to the mutator_lock_.
-  void SuspendAll(const char* cause)
+  // If long suspend is true, then other people who try to suspend will never timeout. Long suspend
+  // is currenly used for hprof since large heaps take a long time.
+  void SuspendAll(const char* cause, bool long_suspend = false)
       EXCLUSIVE_LOCK_FUNCTION(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_,
                      Locks::thread_suspend_count_lock_);
@@ -184,6 +186,9 @@
   // by mutator lock ensures no thread can read when another thread is modifying it.
   Histogram<uint64_t> suspend_all_historam_ GUARDED_BY(Locks::mutator_lock_);
 
+  // Whether or not the current thread suspension is long.
+  bool long_suspend_;
+
   friend class Thread;
 
   DISALLOW_COPY_AND_ASSIGN(ThreadList);