ART: heap counter check

Avoid potential CHECK failure updating num_bytes_freed_revoke_.

Bug: 31023171
Test: art/test.py --host --64 -j32
Change-Id: Ic3fb621c88f5b858f7b4a3ed1aaa1eef36b1e481
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 52afb38..a64a1e7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3727,13 +3727,21 @@
   task_processor_->AddTask(self, added_task);
 }
 
+void Heap::IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke) {
+  size_t previous_num_bytes_freed_revoke =
+      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
+  // Check the updated value is less than the number of bytes allocated. There is a risk of
+  // execution being suspended between the increment above and the CHECK below, leading to
+  // the use of previous_num_bytes_freed_revoke in the comparison.
+  CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
+           previous_num_bytes_freed_revoke + freed_bytes_revoke);
+}
+
 void Heap::RevokeThreadLocalBuffers(Thread* thread) {
   if (rosalloc_space_ != nullptr) {
     size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);
     if (freed_bytes_revoke > 0U) {
-      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
-      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
-               num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
     }
   }
   if (bump_pointer_space_ != nullptr) {
@@ -3748,9 +3756,7 @@
   if (rosalloc_space_ != nullptr) {
     size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);
     if (freed_bytes_revoke > 0U) {
-      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
-      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
-               num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
     }
   }
 }
@@ -3759,9 +3765,7 @@
   if (rosalloc_space_ != nullptr) {
     size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers();
     if (freed_bytes_revoke > 0U) {
-      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
-      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
-               num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
     }
   }
   if (bump_pointer_space_ != nullptr) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9af57d1..ef1c088 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1091,6 +1091,8 @@
     return max_free_;
   }
 
+  ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke);
+
   void TraceHeapSize(size_t heap_size);
 
   // Remove a vlog code from heap-inl.h which is transitively included in half the world.