Add ScopedThreadSuspension

Fixes the TransitionFromRunnableToSuspended and
TransitionFromSuspendedToRunnable pattern that was prone to errors.

Change-Id: Ie6ae9c0357c83b4fc4899d05dfa0975553170267
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 95fcb67..fa58418 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -454,15 +454,13 @@
   uintptr_t saved_dex_pc = locking_dex_pc_;
   locking_dex_pc_ = 0;
 
-  /*
-   * Update thread state. If the GC wakes up, it'll ignore us, knowing
-   * that we won't touch any references in this state, and we'll check
-   * our suspend mode before we transition out.
-   */
-  self->TransitionFromRunnableToSuspended(why);
-
   bool was_interrupted = false;
   {
+    // Update thread state. If the GC wakes up, it'll ignore us, knowing
+    // that we won't touch any references in this state, and we'll check
+    // our suspend mode before we transition out.
+    ScopedThreadSuspension sts(self, why);
+
     // Pseudo-atomically wait on self's wait_cond_ and release the monitor lock.
     MutexLock mu(self, *self->GetWaitMutex());
 
@@ -494,9 +492,6 @@
     }
   }
 
-  // Set self->status back to kRunnable, and self-suspend if needed.
-  self->TransitionFromSuspendedToRunnable();
-
   {
     // We reset the thread's wait_monitor_ field after transitioning back to runnable so
     // that a thread in a waiting/sleeping state has a non-null wait_monitor_ for debugging
@@ -667,9 +662,11 @@
     // Suspend the owner, inflate. First change to blocked and give up mutator_lock_.
     self->SetMonitorEnterObject(obj.Get());
     bool timed_out;
-    self->TransitionFromRunnableToSuspended(kBlocked);
-    Thread* owner = thread_list->SuspendThreadByThreadId(owner_thread_id, false, &timed_out);
-    self->TransitionFromSuspendedToRunnable();
+    Thread* owner;
+    {
+      ScopedThreadSuspension sts(self, kBlocked);
+      owner = thread_list->SuspendThreadByThreadId(owner_thread_id, false, &timed_out);
+    }
     if (owner != nullptr) {
       // We succeeded in suspending the thread, check the lock's status didn't change.
       lock_word = obj->GetLockWord(true);