Revert "[vm] Don't block OOB messages during field initialization; make interrupts lock-free."

This reverts commit 4d789f60d1355d4dc772dd6ba9a5eeecc901a022.

Reason for revert: TSAN races
https://github.com/dart-lang/sdk/issues/46596

Original change's description:
> [vm] Don't block OOB messages during field initialization; make interrupts lock-free.
>
> TEST=ci
> Change-Id: I51d0f51995e197ab71c059f17d22259eea19c286
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206566
> Commit-Queue: Ryan Macnak <rmacnak@google.com>
> Reviewed-by: Ben Konyi <bkonyi@google.com>

TBR=bkonyi@google.com,rmacnak@google.com

Change-Id: I777f5a0bbeb3cd60e03aff46183b19ab4e286664
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206545
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/platform/atomic.h b/runtime/platform/atomic.h
index ffb5bdc..98bc900 100644
--- a/runtime/platform/atomic.h
+++ b/runtime/platform/atomic.h
@@ -21,16 +21,9 @@
   T load(std::memory_order order = std::memory_order_relaxed) const {
     return value_.load(order);
   }
-  T load(std::memory_order order = std::memory_order_relaxed) const volatile {
-    return value_.load(order);
-  }
   void store(T arg, std::memory_order order = std::memory_order_relaxed) {
     value_.store(arg, order);
   }
-  void store(T arg,
-             std::memory_order order = std::memory_order_relaxed) volatile {
-    value_.store(arg, order);
-  }
 
   T fetch_add(T arg, std::memory_order order = std::memory_order_relaxed) {
     return value_.fetch_add(arg, order);
@@ -51,12 +44,6 @@
       std::memory_order order = std::memory_order_relaxed) {
     return value_.compare_exchange_weak(expected, desired, order, order);
   }
-  bool compare_exchange_weak(
-      T& expected,  // NOLINT
-      T desired,
-      std::memory_order order = std::memory_order_relaxed) volatile {
-    return value_.compare_exchange_weak(expected, desired, order, order);
-  }
   bool compare_exchange_strong(
       T& expected,  // NOLINT
       T desired,
diff --git a/runtime/vm/heap/safepoint.cc b/runtime/vm/heap/safepoint.cc
index 52da48b..4a7e5de 100644
--- a/runtime/vm/heap/safepoint.cc
+++ b/runtime/vm/heap/safepoint.cc
@@ -155,7 +155,7 @@
       if (!Thread::IsAtSafepoint(level_, state)) {
         // Send OOB message to get it to safepoint.
         if (current->IsMutatorThread()) {
-          current->ScheduleInterrupts(Thread::kVMInterrupt);
+          current->ScheduleInterruptsLocked(Thread::kVMInterrupt);
         }
         MonitorLocker sl(&parked_lock_);
         num_threads_not_parked_++;
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 8993e5f..cb00d29 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -867,6 +867,19 @@
   return Isolate::IsSystemIsolate(isolate);
 }
 
+NoOOBMessageScope::NoOOBMessageScope(Thread* thread)
+    : ThreadStackResource(thread) {
+  if (thread->isolate() != nullptr) {
+    thread->DeferOOBMessageInterrupts();
+  }
+}
+
+NoOOBMessageScope::~NoOOBMessageScope() {
+  if (thread()->isolate() != nullptr) {
+    thread()->RestoreOOBMessageInterrupts();
+  }
+}
+
 Bequest::~Bequest() {
   if (handle_ == nullptr) {
     return;
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 4c2d9f5..c17a81d 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -134,6 +134,16 @@
   DISALLOW_COPY_AND_ASSIGN(LambdaCallable);
 };
 
+// Disallow OOB message handling within this scope.
+class NoOOBMessageScope : public ThreadStackResource {
+ public:
+  explicit NoOOBMessageScope(Thread* thread);
+  ~NoOOBMessageScope();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NoOOBMessageScope);
+};
+
 // Fixed cache for exception handler lookup.
 typedef FixedCache<intptr_t, ExceptionHandlerInfo, 16> HandlerInfoCache;
 // Fixed cache for catch entry state lookup.
diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc
index 2003591..9402106 100644
--- a/runtime/vm/isolate_test.cc
+++ b/runtime/vm/isolate_test.cc
@@ -145,4 +145,106 @@
   barrier.Exit();
 }
 
+class IsolateTestHelper {
+ public:
+  static uword GetStackLimit(Thread* thread) { return thread->stack_limit_; }
+  static uword GetSavedStackLimit(Thread* thread) {
+    return thread->saved_stack_limit_;
+  }
+  static uword GetDeferredInterruptsMask(Thread* thread) {
+    return thread->deferred_interrupts_mask_;
+  }
+  static uword GetDeferredInterrupts(Thread* thread) {
+    return thread->deferred_interrupts_;
+  }
+};
+
+TEST_CASE(NoOOBMessageScope) {
+  // Finish any GC in progress so that no kVMInterrupt is added for GC reasons.
+  {
+    TransitionNativeToVM transition(thread);
+    GCTestHelper::CollectAllGarbage();
+    const Error& error = Error::Handle(thread->HandleInterrupts());
+    RELEASE_ASSERT(error.IsNull());
+  }
+
+  // EXPECT_EQ is picky about type agreement for its arguments.
+  const uword kZero = 0;
+  const uword kMessageInterrupt = Thread::kMessageInterrupt;
+  const uword kVMInterrupt = Thread::kVMInterrupt;
+  uword stack_limit;
+  uword interrupt_bits;
+
+  // Initially no interrupts are scheduled or deferred.
+  EXPECT_EQ(IsolateTestHelper::GetStackLimit(thread),
+            IsolateTestHelper::GetSavedStackLimit(thread));
+  EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterruptsMask(thread));
+  EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterrupts(thread));
+
+  {
+    // Defer message interrupts.
+    NoOOBMessageScope no_msg_scope(thread);
+    EXPECT_EQ(IsolateTestHelper::GetStackLimit(thread),
+              IsolateTestHelper::GetSavedStackLimit(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterruptsMask(thread));
+    EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterrupts(thread));
+
+    // Schedule a message, it is deferred.
+    thread->ScheduleInterrupts(Thread::kMessageInterrupt);
+    EXPECT_EQ(IsolateTestHelper::GetStackLimit(thread),
+              IsolateTestHelper::GetSavedStackLimit(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterruptsMask(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterrupts(thread));
+
+    // Schedule a vm interrupt, it is not deferred.
+    thread->ScheduleInterrupts(Thread::kVMInterrupt);
+    stack_limit = IsolateTestHelper::GetStackLimit(thread);
+    EXPECT_NE(stack_limit, IsolateTestHelper::GetSavedStackLimit(thread));
+    EXPECT((stack_limit & Thread::kVMInterrupt) != 0);
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterruptsMask(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterrupts(thread));
+
+    // Clear the vm interrupt.  Message is still deferred.
+    interrupt_bits = thread->GetAndClearInterrupts();
+    EXPECT_EQ(kVMInterrupt, interrupt_bits);
+    EXPECT_EQ(IsolateTestHelper::GetStackLimit(thread),
+              IsolateTestHelper::GetSavedStackLimit(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterruptsMask(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterrupts(thread));
+  }
+
+  // Restore message interrupts.  Message is now pending.
+  stack_limit = IsolateTestHelper::GetStackLimit(thread);
+  EXPECT_NE(stack_limit, IsolateTestHelper::GetSavedStackLimit(thread));
+  EXPECT((stack_limit & Thread::kMessageInterrupt) != 0);
+  EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterruptsMask(thread));
+  EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterrupts(thread));
+
+  {
+    // Defer message interrupts, again.  The pending interrupt is deferred.
+    NoOOBMessageScope no_msg_scope(thread);
+    EXPECT_EQ(IsolateTestHelper::GetStackLimit(thread),
+              IsolateTestHelper::GetSavedStackLimit(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterruptsMask(thread));
+    EXPECT_EQ(kMessageInterrupt,
+              IsolateTestHelper::GetDeferredInterrupts(thread));
+  }
+
+  // Restore, then clear interrupts.  The world is as it was.
+  interrupt_bits = thread->GetAndClearInterrupts();
+  EXPECT_EQ(kMessageInterrupt, interrupt_bits);
+  EXPECT_EQ(IsolateTestHelper::GetStackLimit(thread),
+            IsolateTestHelper::GetSavedStackLimit(thread));
+  EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterruptsMask(thread));
+  EXPECT_EQ(kZero, IsolateTestHelper::GetDeferredInterrupts(thread));
+}
+
 }  // namespace dart
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 8c08ad9..7c9214a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -11162,6 +11162,7 @@
   }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  NoOOBMessageScope no_msg_scope(thread);
   NoReloadScope no_reload_scope(thread);
   const Function& initializer = Function::Handle(EnsureInitializerFunction());
   return DartEntry::InvokeFunction(initializer, Object::empty_array());
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index 45d99f9..16a1f8f 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -91,6 +91,9 @@
       no_safepoint_scope_depth_(0),
 #endif
       reusable_handles_(),
+      defer_oob_messages_count_(0),
+      deferred_interrupts_mask_(0),
+      deferred_interrupts_(0),
       stack_overflow_count_(0),
       hierarchy_info_(NULL),
       type_usage_info_(NULL),
@@ -395,7 +398,7 @@
   MonitorLocker ml(&thread_lock_);
   if (!HasScheduledInterrupts()) {
     // No interrupt pending, set stack_limit_ too.
-    stack_limit_.store(limit);
+    stack_limit_ = limit;
   }
   saved_stack_limit_ = limit;
 }
@@ -405,33 +408,95 @@
 }
 
 void Thread::ScheduleInterrupts(uword interrupt_bits) {
+  MonitorLocker ml(&thread_lock_);
+  ScheduleInterruptsLocked(interrupt_bits);
+}
+
+void Thread::ScheduleInterruptsLocked(uword interrupt_bits) {
+  ASSERT(thread_lock_.IsOwnedByCurrentThread());
   ASSERT((interrupt_bits & ~kInterruptsMask) == 0);  // Must fit in mask.
 
-  uword old_limit = stack_limit_.load();
-  uword new_limit;
-  do {
-    if (old_limit == saved_stack_limit_) {
-      new_limit = (kInterruptStackLimit & ~kInterruptsMask) | interrupt_bits;
-    } else {
-      new_limit = old_limit | interrupt_bits;
+  // Check to see if any of the requested interrupts should be deferred.
+  uword defer_bits = interrupt_bits & deferred_interrupts_mask_;
+  if (defer_bits != 0) {
+    deferred_interrupts_ |= defer_bits;
+    interrupt_bits &= ~deferred_interrupts_mask_;
+    if (interrupt_bits == 0) {
+      return;
     }
-  } while (!stack_limit_.compare_exchange_weak(old_limit, new_limit));
+  }
+
+  if (stack_limit_ == saved_stack_limit_) {
+    stack_limit_ = (kInterruptStackLimit & ~kInterruptsMask) | interrupt_bits;
+  } else {
+    stack_limit_ = stack_limit_ | interrupt_bits;
+  }
 }
 
 uword Thread::GetAndClearInterrupts() {
-  uword interrupt_bits = 0;
-  uword old_limit = stack_limit_.load();
-  uword new_limit = saved_stack_limit_;
-  do {
-    if (old_limit == saved_stack_limit_) {
-      return interrupt_bits;
-    }
-    interrupt_bits = interrupt_bits | (old_limit & kInterruptsMask);
-  } while (!stack_limit_.compare_exchange_weak(old_limit, new_limit));
-
+  MonitorLocker ml(&thread_lock_);
+  if (stack_limit_ == saved_stack_limit_) {
+    return 0;  // No interrupt was requested.
+  }
+  uword interrupt_bits = stack_limit_ & kInterruptsMask;
+  stack_limit_ = saved_stack_limit_;
   return interrupt_bits;
 }
 
+void Thread::DeferOOBMessageInterrupts() {
+  MonitorLocker ml(&thread_lock_);
+  defer_oob_messages_count_++;
+  if (defer_oob_messages_count_ > 1) {
+    // OOB message interrupts are already deferred.
+    return;
+  }
+  ASSERT(deferred_interrupts_mask_ == 0);
+  deferred_interrupts_mask_ = kMessageInterrupt;
+
+  if (stack_limit_ != saved_stack_limit_) {
+    // Defer any interrupts which are currently pending.
+    deferred_interrupts_ = stack_limit_ & deferred_interrupts_mask_;
+
+    // Clear deferrable interrupts, if present.
+    stack_limit_ = stack_limit_ & ~deferred_interrupts_mask_;
+
+    if ((stack_limit_ & kInterruptsMask) == 0) {
+      // No other pending interrupts.  Restore normal stack limit.
+      stack_limit_ = saved_stack_limit_;
+    }
+  }
+#if !defined(PRODUCT)
+  if (FLAG_trace_service && FLAG_trace_service_verbose) {
+    OS::PrintErr("[+%" Pd64 "ms] Isolate %s deferring OOB interrupts\n",
+                 Dart::UptimeMillis(), isolate()->name());
+  }
+#endif  // !defined(PRODUCT)
+}
+
+void Thread::RestoreOOBMessageInterrupts() {
+  MonitorLocker ml(&thread_lock_);
+  defer_oob_messages_count_--;
+  if (defer_oob_messages_count_ > 0) {
+    return;
+  }
+  ASSERT(defer_oob_messages_count_ == 0);
+  ASSERT(deferred_interrupts_mask_ == kMessageInterrupt);
+  deferred_interrupts_mask_ = 0;
+  if (deferred_interrupts_ != 0) {
+    if (stack_limit_ == saved_stack_limit_) {
+      stack_limit_ = kInterruptStackLimit & ~kInterruptsMask;
+    }
+    stack_limit_ = stack_limit_ | deferred_interrupts_;
+    deferred_interrupts_ = 0;
+  }
+#if !defined(PRODUCT)
+  if (FLAG_trace_service && FLAG_trace_service_verbose) {
+    OS::PrintErr("[+%" Pd64 "ms] Isolate %s restoring OOB interrupts\n",
+                 Dart::UptimeMillis(), isolate()->name());
+  }
+#endif  // !defined(PRODUCT)
+}
+
 ErrorPtr Thread::HandleInterrupts() {
   uword interrupt_bits = GetAndClearInterrupts();
   if ((interrupt_bits & kVMInterrupt) != 0) {
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 6e3ff67..2d80830 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -417,10 +417,11 @@
   };
 
   void ScheduleInterrupts(uword interrupt_bits);
+  void ScheduleInterruptsLocked(uword interrupt_bits);
   ErrorPtr HandleInterrupts();
   uword GetAndClearInterrupts();
   bool HasScheduledInterrupts() const {
-    return (stack_limit_.load() & kInterruptsMask) != 0;
+    return (stack_limit_ & kInterruptsMask) != 0;
   }
 
   // Monitor corresponding to this thread.
@@ -1030,7 +1031,7 @@
   // in SIMARM(IA32) and ARM, and the same offsets in SIMARM64(X64) and ARM64.
   // We use only word-sized fields to avoid differences in struct packing on the
   // different architectures. See also CheckOffsets in dart.cc.
-  volatile RelaxedAtomic<uword> stack_limit_;
+  RelaxedAtomic<uword> stack_limit_;
   uword write_barrier_mask_;
   uword heap_base_;
   Isolate* isolate_;
@@ -1106,6 +1107,9 @@
   int32_t no_safepoint_scope_depth_;
 #endif
   VMHandles reusable_handles_;
+  intptr_t defer_oob_messages_count_;
+  uint16_t deferred_interrupts_mask_;
+  uint16_t deferred_interrupts_;
   int32_t stack_overflow_count_;
   uint32_t runtime_call_count_ = 0;
 
@@ -1201,6 +1205,9 @@
 
   static void SetCurrent(Thread* current) { OSThread::SetCurrentTLS(current); }
 
+  void DeferOOBMessageInterrupts();
+  void RestoreOOBMessageInterrupts();
+
 #define REUSABLE_FRIEND_DECLARATION(name)                                      \
   friend class Reusable##name##HandleScope;
   REUSABLE_HANDLE_LIST(REUSABLE_FRIEND_DECLARATION)
@@ -1211,7 +1218,9 @@
   friend class InterruptChecker;
   friend class Isolate;
   friend class IsolateGroup;
+  friend class IsolateTestHelper;
   friend class NoActiveIsolateScope;
+  friend class NoOOBMessageScope;
   friend class NoReloadScope;
   friend class Simulator;
   friend class StackZone;
diff --git a/tests/lib/isolate/kill_infinite_loop_in_initializer_test.dart b/tests/lib/isolate/kill_infinite_loop_in_initializer_test.dart
deleted file mode 100644
index 321fbda4..0000000
--- a/tests/lib/isolate/kill_infinite_loop_in_initializer_test.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// VMOptions=--enable-isolate-groups --experimental-enable-isolate-groups-jit
-// VMOptions=--no-enable-isolate-groups
-
-// Regression test against out-of-band messages being blocked during lazy
-// static field initialization.
-
-import "dart:isolate";
-import "dart:async";
-import "package:expect/expect.dart";
-import "package:async_helper/async_helper.dart";
-
-dynamic staticFieldWithBadInitializer = badInitializer();
-
-badInitializer() {
-  print("badInitializer");
-  for (;;) {}
-  return 42; // Unreachable.
-}
-
-child(message) {
-  print("child");
-  RawReceivePort port = new RawReceivePort();
-  print(staticFieldWithBadInitializer);
-  port.close(); // Unreachable.
-}
-
-void main() {
-  asyncStart();
-  Isolate.spawn(child, null).then((Isolate isolate) {
-    print("spawned");
-    late RawReceivePort exitSignal;
-    exitSignal = new RawReceivePort((_) {
-      print("onExit");
-      exitSignal.close();
-      asyncEnd();
-    });
-    isolate.addOnExitListener(exitSignal.sendPort);
-    isolate.kill(priority: Isolate.immediate);
-  });
-}
diff --git a/tests/lib_2/isolate/kill_infinite_loop_in_initializer_test.dart b/tests/lib_2/isolate/kill_infinite_loop_in_initializer_test.dart
deleted file mode 100644
index af8650e..0000000
--- a/tests/lib_2/isolate/kill_infinite_loop_in_initializer_test.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// VMOptions=--enable-isolate-groups --experimental-enable-isolate-groups-jit
-// VMOptions=--no-enable-isolate-groups
-
-// Regression test against out-of-band messages being blocked during lazy
-// static field initialization.
-
-import "dart:isolate";
-import "dart:async";
-import "package:expect/expect.dart";
-import "package:async_helper/async_helper.dart";
-
-dynamic staticFieldWithBadInitializer = badInitializer();
-
-badInitializer() {
-  print("badInitializer");
-  for (;;) {}
-  return 42; // Unreachable.
-}
-
-child(message) {
-  print("child");
-  RawReceivePort port = new RawReceivePort();
-  print(staticFieldWithBadInitializer);
-  port.close(); // Unreachable.
-}
-
-void main() {
-  asyncStart();
-  Isolate.spawn(child, null).then((Isolate isolate) {
-    print("spawned");
-    RawReceivePort exitSignal;
-    exitSignal = new RawReceivePort((_) {
-      print("onExit");
-      exitSignal.close();
-      asyncEnd();
-    });
-    isolate.addOnExitListener(exitSignal.sendPort);
-    isolate.kill(priority: Isolate.immediate);
-  });
-}