Add more checking to WrappedSuspend1Barrier

Bug: 323668816
Test: Treehugger
Change-Id: I5edf53ecb6cf3243b07164e19d63a8efb355281c
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 2fcc4b0..57d606c 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -279,7 +279,21 @@
   }
 }
 
+inline void Thread::CheckBarrierInactive(WrappedSuspend1Barrier* suspend1_barrier) {
+  for (WrappedSuspend1Barrier* w = tlsPtr_.active_suspend1_barriers; w != nullptr; w = w->next_) {
+    CHECK_EQ(w->magic_, WrappedSuspend1Barrier::kMagic)
+        << "first = " << tlsPtr_.active_suspend1_barriers << " current = " << w
+        << " next = " << w->next_;
+    CHECK_NE(w, suspend1_barrier);
+  }
+}
+
 inline void Thread::AddSuspend1Barrier(WrappedSuspend1Barrier* suspend1_barrier) {
+  if (tlsPtr_.active_suspend1_barriers != nullptr) {
+    CHECK_EQ(tlsPtr_.active_suspend1_barriers->magic_, WrappedSuspend1Barrier::kMagic)
+        << "first = " << tlsPtr_.active_suspend1_barriers;
+  }
+  CHECK_EQ(suspend1_barrier->magic_, WrappedSuspend1Barrier::kMagic);
   suspend1_barrier->next_ = tlsPtr_.active_suspend1_barriers;
   tlsPtr_.active_suspend1_barriers = suspend1_barrier;
 }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 0363288..2ebbe13 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1513,6 +1513,9 @@
       tlsPtr_.active_suspendall_barrier = nullptr;
     }
     for (WrappedSuspend1Barrier* w = tlsPtr_.active_suspend1_barriers; w != nullptr; w = w->next_) {
+      CHECK_EQ(w->magic_, WrappedSuspend1Barrier::kMagic)
+          << "first = " << tlsPtr_.active_suspend1_barriers << " current = " << w
+          << " next = " << w->next_;
       pass_barriers.push_back(&(w->barrier_));
     }
     tlsPtr_.active_suspend1_barriers = nullptr;
@@ -1685,9 +1688,9 @@
   // Although this is a thread suspension, the target thread only blocks while we run the
   // checkpoint, which is presumed to terminate quickly even if other threads are blocked.
   // Note: IncrementSuspendCount also expects the thread_list_lock to be held unless this == self.
+  WrappedSuspend1Barrier wrapped_barrier{};
   {
     bool is_suspended = false;
-    WrappedSuspend1Barrier wrapped_barrier{};
 
     {
       MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
@@ -1767,6 +1770,9 @@
     DCHECK_NE(GetState(), ThreadState::kRunnable);
     DCHECK_GT(GetSuspendCount(), 0);
     DecrementSuspendCount(self);
+    if (kIsDebugBuild) {
+      CheckBarrierInactive(&wrapped_barrier);
+    }
     resume_cond_->Broadcast(self);
   }
 
diff --git a/runtime/thread.h b/runtime/thread.h
index 5dcdb8b..8bcd815 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -199,8 +199,11 @@
 
 // See Thread.tlsPtr_.active_suspend1_barriers below for explanation.
 struct WrappedSuspend1Barrier {
-  WrappedSuspend1Barrier() : barrier_(1), next_(nullptr) {}
-  AtomicInteger barrier_;  // Only updated while holding thread_suspend_count_lock_ .
+  // TODO(b/23668816): At least weaken CHECKs to DCHECKs once the bug is fixed.
+  static constexpr int kMagic = 0xba8;
+  WrappedSuspend1Barrier() : magic_(kMagic), barrier_(1), next_(nullptr) {}
+  int magic_;
+  AtomicInteger barrier_;
   struct WrappedSuspend1Barrier* next_ GUARDED_BY(Locks::thread_suspend_count_lock_);
 };
 
@@ -1769,6 +1772,10 @@
 
   ALWAYS_INLINE bool HasActiveSuspendBarrier() REQUIRES(Locks::thread_suspend_count_lock_);
 
+  // CHECK that the given barrier is no longer on our list.
+  ALWAYS_INLINE void CheckBarrierInactive(WrappedSuspend1Barrier* suspend1_barrier)
+      REQUIRES(Locks::thread_suspend_count_lock_);
+
   // Registers the current thread as the jit sensitive thread. Should be called just once.
   static void SetJitSensitiveThread() {
     if (jit_sensitive_thread_ == nullptr) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index b841eaa..02e3f54 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1089,6 +1089,8 @@
           if (thread->IsSuspended()) {
             // See the discussion in mutator_gc_coord.md and SuspendAllInternal for the race here.
             thread->RemoveFirstSuspend1Barrier(&wrapped_barrier);
+            // PassActiveSuspendBarriers couldn't have seen our barrier, since it also acquires
+            // 'thread_suspend_count_lock_'. `wrapped_barrier` will not be accessed.
             if (!thread->HasActiveSuspendBarrier()) {
               thread->AtomicClearFlag(ThreadFlag::kActiveSuspendBarrier);
             }
@@ -1185,7 +1187,7 @@
     }
     is_suspended = true;
   }
-  // wrapped_barrier.barrier_ has been decremented and will no longer be accessed.
+  // wrapped_barrier.barrier_ will no longer be accessed.
   VLOG(threads) << func_name << " suspended: " << *thread;
   if (ATraceEnabled()) {
     std::string name;
@@ -1194,7 +1196,11 @@
         StringPrintf("%s suspended %s for tid=%d", func_name, name.c_str(), thread->GetTid())
             .c_str());
   }
-  DCHECK(thread->IsSuspended());
+  if (kIsDebugBuild) {
+    CHECK(thread->IsSuspended());
+    MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
+    thread->CheckBarrierInactive(&wrapped_barrier);
+  }
   return true;
 }