[gardening] Make timing-dependent vm/cc/SafepointOperation_SafepointPointTest more robust

This test of safepoint operations has some probability of failing due to
dependence on timing of events.

This CL will lower probability of test failure by ensuring the helper
threads will try hard to join in normal way (and fail to do so if
expected) before unblocking main thread to continue.

Issue https://github.com/dart-lang/sdk/issues/48716

TEST=ci

Change-Id: Ibd9b8573248bd2efc0c45525affa640adb7517a8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239665
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/heap/safepoint_test.cc b/runtime/vm/heap/safepoint_test.cc
index 15eedc5..0693ec1 100644
--- a/runtime/vm/heap/safepoint_test.cc
+++ b/runtime/vm/heap/safepoint_test.cc
@@ -316,27 +316,38 @@
 
     uword last_sync = OS::GetCurrentTimeMillis();
     while (!data()->IsIn(kPleaseExit)) {
+      OS::SleepMicros(100);  // Make test avoid consuming 100% CPU x kTaskCount.
       switch (data()->level) {
         case SafepointLevel::kGC: {
           // This thread should join only GC safepoint operations.
           RuntimeCallDeoptScope no_deopt(
               Thread::Current(), RuntimeCallDeoptAbility::kCannotLazyDeopt);
-          SafepointIfRequested(thread_, data()->gc_only_checkins);
+          if (SafepointIfRequested(thread_, data()->gc_only_checkins)) {
+            last_sync = OS::GetCurrentTimeMillis();
+          }
           break;
         }
         case SafepointLevel::kGCAndDeopt: {
           // This thread should join any safepoint operations.
-          SafepointIfRequested(thread_, data()->deopt_checkin);
+          if (SafepointIfRequested(thread_, data()->deopt_checkin)) {
+            last_sync = OS::GetCurrentTimeMillis();
+          }
           break;
         }
         case SafepointLevel::kNumLevels:
           UNREACHABLE();
       }
 
-      // If we are asked to join a deopt safepoint operation we will comply with
-      // that but only every second.
+      // If the main thread asks us to join a deopt safepoint but we are
+      // instructed to only really collaborate with GC safepoints we won't
+      // participate in the above cases (and therefore not register our
+      // check-in by increasing the checkin counts).
+      //
+      // After being quite sure to not have joined deopt safepoint if we only
+      // support GC safepoints, we will eventually comply here to make main
+      // thread continue.
       const auto now = OS::GetCurrentTimeMillis();
-      if ((now - last_sync) > 200) {
+      if ((now - last_sync) > 1000) {
         thread_->EnterSafepoint();
         thread_->ExitSafepoint();
         last_sync = now;
@@ -344,13 +355,14 @@
     }
   }
 
-  void SafepointIfRequested(Thread* thread, std::atomic<intptr_t>* checkins) {
-    OS::SleepMicros(10);
+  bool SafepointIfRequested(Thread* thread, std::atomic<intptr_t>* checkins) {
     if (thread->IsSafepointRequested()) {
       // Collaborates by checking into the safepoint.
       thread->BlockForSafepoint();
       (*checkins)++;
+      return true;
     }
+    return false;
   }
 };
 
@@ -392,11 +404,11 @@
     }
     {
       { GcSafepointOperationScope safepoint_operation(thread); }
-      OS::SleepMicros(500);
+      OS::SleepMicros(1000);  // Wait for threads to exit safepoint
       { DeoptSafepointOperationScope safepoint_operation(thread); }
-      OS::SleepMicros(500);
+      OS::SleepMicros(1000);  // Wait for threads to exit safepoint
       { GcSafepointOperationScope safepoint_operation(thread); }
-      OS::SleepMicros(500);
+      OS::SleepMicros(1000);  // Wait for threads to exit safepoint
       { DeoptSafepointOperationScope safepoint_operation(thread); }
     }
     for (intptr_t i = 0; i < kTaskCount; i++) {