[vm] More TSAN profiler issues.

TEST=tsan
Bug: https://github.com/dart-lang/sdk/issues/61594
Change-Id: I82598218a98b764d66deb671f8ec841c8e1c3cab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452280
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index b608748..056de9b 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -1195,6 +1195,9 @@
   void set_current_sample_block(SampleBlock* block) {
     current_sample_block_ = block;
   }
+  SampleBlock* exchange_current_sample_block(SampleBlock* block) {
+    return current_sample_block_.exchange(block, std::memory_order_acq_rel);
+  }
   void ProcessFreeSampleBlocks(Thread* thread);
 
   // Returns the current SampleBlock used to track Dart allocation samples.
@@ -1204,6 +1207,10 @@
   void set_current_allocation_sample_block(SampleBlock* block) {
     current_allocation_sample_block_ = block;
   }
+  SampleBlock* exchange_current_allocation_sample_block(SampleBlock* block) {
+    return current_allocation_sample_block_.exchange(block,
+                                                     std::memory_order_acq_rel);
+  }
 
   bool TakeHasCompletedBlocks() {
     return has_completed_blocks_.exchange(0) != 0;
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index 1b43c09..e73077f 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -794,15 +794,14 @@
 static void FlushSampleBlocks(Isolate* isolate) {
   ASSERT(isolate != nullptr);
 
-  SampleBlock* block = isolate->current_sample_block();
+  SampleBlock* block = isolate->exchange_current_sample_block(nullptr);
   if (block != nullptr) {
-    isolate->set_current_sample_block(nullptr);
     block->MarkCompleted();
   }
 
-  block = isolate->current_allocation_sample_block();
+  block = isolate->exchange_current_allocation_sample_block(nullptr);
   if (block != nullptr) {
-    isolate->set_current_allocation_sample_block(nullptr);
+    // Allocation samples are collected synchronously.
     block->MarkCompleted();
   }
 }
@@ -1416,6 +1415,25 @@
   sample->SetAt(0, pc);
 }
 
+void ReleaseToCurrentBlock(Isolate* isolate) {
+#if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_WINDOWS) ||            \
+    defined(DART_HOST_OS_FUCHSIA)
+  // The sample is collected by a different thread. The sample appears all at
+  // once from the profiled thread's point of view. Establish the isolate
+  // flushing its own current block happens-after the most recent sample
+  // written in that block by dumping a dependency through the current block.
+  // TSAN doesn't otherwise know this is already true because it doesn't have
+  // special treatment for thread_suspend/resume.
+  SampleBlock* block = isolate->current_sample_block();
+  isolate->exchange_current_sample_block(block);
+#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
+  // The sample is collected by a signal handler on the same thread being
+  // sampled.
+#else
+#error What kind of sampler?
+#endif
+}
+
 void Profiler::SampleThread(Thread* thread,
                             const InterruptedThreadState& state) {
   ASSERT(thread != nullptr);
@@ -1494,6 +1512,7 @@
     if (thread->IGNORE_RACE2(IsDeoptimizing)()) {
       counters_.single_frame_sample_deoptimizing.fetch_add(1);
       SampleThreadSingleFrame(thread, sample, pc);
+      ReleaseToCurrentBlock(isolate);
       return;
     }
   }
@@ -1505,6 +1524,7 @@
     counters_.single_frame_sample_get_and_validate_stack_bounds.fetch_add(1);
     // Could not get stack boundary.
     SampleThreadSingleFrame(thread, sample, pc);
+    ReleaseToCurrentBlock(isolate);
     return;
   }
 
@@ -1531,6 +1551,7 @@
   CollectSample(isolate, exited_dart_code, in_dart_code, sample,
                 &native_stack_walker, &dart_stack_walker, pc, fp, sp,
                 &counters_);
+  ReleaseToCurrentBlock(isolate);
 }
 
 CodeDescriptor::CodeDescriptor(const AbstractCode code) : code_(code) {}
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index e72278b..de90bd6 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -4,6 +4,7 @@
 
 #include "platform/assert.h"
 
+#include "platform/thread_sanitizer.h"
 #include "vm/dart_api_impl.h"
 #include "vm/dart_api_state.h"
 #include "vm/flags.h"
@@ -29,19 +30,27 @@
 // SampleVisitor ignores samples with pc == 0.
 const uword kValidPc = 0xFF;
 
+NO_SANITIZE_THREAD
+static void SetIgnoreRace(bool* flag, bool value) {
+  *flag = value;
+}
+
 // Some tests are written assuming native stack trace profiling is disabled.
 class DisableNativeProfileScope : public ValueObject {
  public:
   DisableNativeProfileScope()
       : FLAG_profile_vm_(FLAG_profile_vm),
         FLAG_profile_vm_allocation_(FLAG_profile_vm_allocation) {
-    FLAG_profile_vm = false;
-    FLAG_profile_vm_allocation = false;
+    // Ignore race: we generally don't try to ensure the profiler is disabled
+    // when changing setting for unit tests. On Linux/Android, there is no way
+    // to wait for outstanding SIGPROFs.
+    SetIgnoreRace(&FLAG_profile_vm, false);
+    SetIgnoreRace(&FLAG_profile_vm_allocation, false);
   }
 
   ~DisableNativeProfileScope() {
-    FLAG_profile_vm = FLAG_profile_vm_;
-    FLAG_profile_vm_allocation = FLAG_profile_vm_allocation_;
+    SetIgnoreRace(&FLAG_profile_vm, FLAG_profile_vm_);
+    SetIgnoreRace(&FLAG_profile_vm_allocation, FLAG_profile_vm_allocation_);
   }
 
  private: