Version 2.14.0-314.0.dev

Merge commit '109ba28c6e8af000cdd82589b3bda02755551c1b' into 'dev'
diff --git a/pkg/analyzer/lib/src/context/builder.dart b/pkg/analyzer/lib/src/context/builder.dart
index e143315..502ea26 100644
--- a/pkg/analyzer/lib/src/context/builder.dart
+++ b/pkg/analyzer/lib/src/context/builder.dart
@@ -6,103 +6,9 @@
 import 'dart:core';
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/packages.dart';
-import 'package:analyzer/src/workspace/basic.dart';
-import 'package:analyzer/src/workspace/bazel.dart';
-import 'package:analyzer/src/workspace/gn.dart';
-import 'package:analyzer/src/workspace/package_build.dart';
-import 'package:analyzer/src/workspace/pub.dart';
-import 'package:analyzer/src/workspace/workspace.dart';
 import 'package:yaml/yaml.dart';
 
-/// A utility class used to build an analysis context for a given directory.
-///
-/// The construction of analysis contexts is as follows:
-///
-/// 1. Determine how package: URI's are to be resolved. This follows the lookup
-///    algorithm defined by the [package specification][1].
-///
-/// 2. Using the results of step 1, look in each package for an embedder file
-///    (_embedder.yaml). If one exists then it defines the SDK. If multiple such
-///    files exist then use the first one found. Otherwise, use the default SDK.
-///
-/// 3. Look for an analysis options file (`analysis_options.yaml`) and process
-///    the options in the file.
-///
-/// 4. Create a new context. Initialize its source factory based on steps 1, 2
-///    and 3. Initialize its analysis options from step 4.
-///
-/// [1]: https://github.com/dart-lang/dart_enhancement_proposals/blob/master/Accepted/0005%20-%20Package%20Specification/DEP-pkgspec.md.
-class ContextBuilder {
-  /// Return [Packages] to analyze a resource with the [rootPath].
-  static Packages createPackageMap({
-    required ResourceProvider resourceProvider,
-    required ContextBuilderOptions options,
-    required String rootPath,
-  }) {
-    var configPath = options.defaultPackageFilePath;
-    if (configPath != null) {
-      var configFile = resourceProvider.getFile(configPath);
-      return parsePackagesFile(resourceProvider, configFile);
-    } else {
-      var resource = resourceProvider.getResource(rootPath);
-      return findPackagesFrom(resourceProvider, resource);
-    }
-  }
-
-  /// If [packages] is provided, it will be used for the [Workspace],
-  /// otherwise the packages file from [options] will be used, or discovered
-  /// from [rootPath].
-  ///
-  /// TODO(scheglov) Make [packages] required, remove [options] and discovery.
-  static Workspace createWorkspace({
-    required ResourceProvider resourceProvider,
-    required ContextBuilderOptions options,
-    Packages? packages,
-    required String rootPath,
-    bool lookForBazelBuildFileSubstitutes = true,
-  }) {
-    packages ??= ContextBuilder.createPackageMap(
-      resourceProvider: resourceProvider,
-      options: options,
-      rootPath: rootPath,
-    );
-    var packageMap = <String, List<Folder>>{};
-    for (var package in packages.packages) {
-      packageMap[package.name] = [package.libFolder];
-    }
-
-    if (_hasPackageFileInPath(resourceProvider, rootPath)) {
-      // A Bazel or Gn workspace that includes a '.packages' file is treated
-      // like a normal (non-Bazel/Gn) directory. But may still use
-      // package:build or Pub.
-      return PackageBuildWorkspace.find(
-              resourceProvider, packageMap, rootPath) ??
-          PubWorkspace.find(resourceProvider, packageMap, rootPath) ??
-          BasicWorkspace.find(resourceProvider, packageMap, rootPath);
-    }
-    Workspace? workspace = BazelWorkspace.find(resourceProvider, rootPath,
-        lookForBuildFileSubstitutes: lookForBazelBuildFileSubstitutes);
-    workspace ??= GnWorkspace.find(resourceProvider, rootPath);
-    workspace ??=
-        PackageBuildWorkspace.find(resourceProvider, packageMap, rootPath);
-    workspace ??= PubWorkspace.find(resourceProvider, packageMap, rootPath);
-    workspace ??= BasicWorkspace.find(resourceProvider, packageMap, rootPath);
-    return workspace;
-  }
-
-  /// Return `true` if either the directory at [rootPath] or a parent of that
-  /// directory contains a `.packages` file.
-  static bool _hasPackageFileInPath(
-      ResourceProvider resourceProvider, String rootPath) {
-    var folder = resourceProvider.getFolder(rootPath);
-    return folder.withAncestors.any((current) {
-      return current.getChildAssumingFile('.packages').exists;
-    });
-  }
-}
-
-/// Options used by a [ContextBuilder].
+/// TODO(scheglov) It is not used, inline it.
 class ContextBuilderOptions {
   /// The file path of the analysis options file that should be used in place of
   /// any file in the root directory or a parent of the root directory, or `null`
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
index c1dfa14..7927eca 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
@@ -10,14 +10,16 @@
 import 'package:analyzer/file_system/physical_file_system.dart'
     show PhysicalResourceProvider;
 import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
-import 'package:analyzer/src/context/builder.dart' as old;
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/dart/analysis/context_root.dart';
 import 'package:analyzer/src/task/options.dart';
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
 import 'package:analyzer/src/util/yaml.dart';
 import 'package:analyzer/src/workspace/basic.dart';
+import 'package:analyzer/src/workspace/bazel.dart';
+import 'package:analyzer/src/workspace/gn.dart';
+import 'package:analyzer/src/workspace/package_build.dart';
+import 'package:analyzer/src/workspace/pub.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
 import 'package:glob/glob.dart';
 import 'package:path/path.dart';
@@ -366,12 +368,34 @@
       packages = Packages.empty;
     }
 
-    return old.ContextBuilder.createWorkspace(
-      resourceProvider: resourceProvider,
-      packages: packages,
-      options: ContextBuilderOptions(), // TODO(scheglov) remove it
-      rootPath: folder.path,
-    );
+    // TODO(scheglov) Can we use Packages instead?
+    var packageMap = <String, List<Folder>>{};
+    for (var package in packages.packages) {
+      packageMap[package.name] = [package.libFolder];
+    }
+
+    var rootPath = folder.path;
+
+    // TODO(scheglov) Do we need this?
+    if (_hasPackageFileInPath(rootPath)) {
+      // A Bazel or Gn workspace that includes a '.packages' file is treated
+      // like a normal (non-Bazel/Gn) directory. But may still use
+      // package:build or Pub.
+      return PackageBuildWorkspace.find(
+              resourceProvider, packageMap, rootPath) ??
+          PubWorkspace.find(resourceProvider, packageMap, rootPath) ??
+          BasicWorkspace.find(resourceProvider, packageMap, rootPath);
+    }
+
+    Workspace? workspace;
+    workspace = BazelWorkspace.find(resourceProvider, rootPath,
+        lookForBuildFileSubstitutes: false);
+    workspace ??= GnWorkspace.find(resourceProvider, rootPath);
+    workspace ??=
+        PackageBuildWorkspace.find(resourceProvider, packageMap, rootPath);
+    workspace ??= PubWorkspace.find(resourceProvider, packageMap, rootPath);
+    workspace ??= BasicWorkspace.find(resourceProvider, packageMap, rootPath);
+    return workspace;
   }
 
   File? _findDefaultOptionsFile(Workspace workspace) {
@@ -491,6 +515,15 @@
     return _getFile(folder, file_paths.dotPackages);
   }
 
+  /// Return `true` if either the directory at [rootPath] or a parent of that
+  /// directory contains a `.packages` file.
+  bool _hasPackageFileInPath(String rootPath) {
+    var folder = resourceProvider.getFolder(rootPath);
+    return folder.withAncestors.any((current) {
+      return current.getChildAssumingFile('.packages').exists;
+    });
+  }
+
   /// Add to the given lists of [folders] and [files] all of the resources in
   /// the given list of [paths] that exist and are not contained within one of
   /// the folders.
diff --git a/pkg/analyzer/lib/src/workspace/simple.dart b/pkg/analyzer/lib/src/workspace/simple.dart
index 51d96d2..02413ce 100644
--- a/pkg/analyzer/lib/src/workspace/simple.dart
+++ b/pkg/analyzer/lib/src/workspace/simple.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/source/package_map_resolver.dart';
@@ -12,9 +11,6 @@
 
 /// An abstract class for simple workspaces which do not feature any build
 /// artifacts or generated files.
-///
-/// The [packageMap] and [packageUrlResolver] are simple derivations from the
-/// [ContextBuilder] and [ResourceProvider] required for the class.
 abstract class SimpleWorkspace extends Workspace {
   /// The [ResourceProvider] by which paths are converted into [Resource]s.
   final ResourceProvider provider;
diff --git a/runtime/platform/atomic.h b/runtime/platform/atomic.h
index 98bc900..ffb5bdc 100644
--- a/runtime/platform/atomic.h
+++ b/runtime/platform/atomic.h
@@ -21,9 +21,16 @@
   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);
@@ -44,6 +51,12 @@
       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 4a7e5de..52da48b 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->ScheduleInterruptsLocked(Thread::kVMInterrupt);
+          current->ScheduleInterrupts(Thread::kVMInterrupt);
         }
         MonitorLocker sl(&parked_lock_);
         num_threads_not_parked_++;
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index cb00d29..8993e5f 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -867,19 +867,6 @@
   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 c17a81d..4c2d9f5 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -134,16 +134,6 @@
   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_reload.cc b/runtime/vm/isolate_reload.cc
index 4bbf74e..7efed86 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -2147,7 +2147,8 @@
         // At that point it doesn't have the field table setup yet.
         if (field_table->IsReadyToUse()) {
           value_ = field_table->At(field_id);
-          if (value_.ptr() != Object::sentinel().ptr()) {
+          if ((value_.ptr() != Object::sentinel().ptr()) &&
+              (value_.ptr() != Object::transition_sentinel().ptr())) {
             CheckValueType(null_safety, value_, field);
           }
         }
diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc
index 9402106..2003591 100644
--- a/runtime/vm/isolate_test.cc
+++ b/runtime/vm/isolate_test.cc
@@ -145,106 +145,4 @@
   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 77f8e4d..3f6d0f7 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -11156,8 +11156,7 @@
 }
 
 ObjectPtr Field::EvaluateInitializer() const {
-  Thread* const thread = Thread::Current();
-  ASSERT(thread->IsMutatorThread());
+  ASSERT(Thread::Current()->IsMutatorThread());
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
   if (is_static() && is_const()) {
@@ -11165,8 +11164,6 @@
   }
 #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 16a1f8f..d03ff5a 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -91,9 +91,6 @@
       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),
@@ -398,7 +395,7 @@
   MonitorLocker ml(&thread_lock_);
   if (!HasScheduledInterrupts()) {
     // No interrupt pending, set stack_limit_ too.
-    stack_limit_ = limit;
+    stack_limit_.store(limit);
   }
   saved_stack_limit_ = limit;
 }
@@ -407,96 +404,40 @@
   SetStackLimit(~static_cast<uword>(0));
 }
 
-void Thread::ScheduleInterrupts(uword interrupt_bits) {
-  MonitorLocker ml(&thread_lock_);
-  ScheduleInterruptsLocked(interrupt_bits);
+static bool IsInterruptLimit(uword limit) {
+  return (limit & ~Thread::kInterruptsMask) ==
+         (kInterruptStackLimit & ~Thread::kInterruptsMask);
 }
 
-void Thread::ScheduleInterruptsLocked(uword interrupt_bits) {
-  ASSERT(thread_lock_.IsOwnedByCurrentThread());
+void Thread::ScheduleInterrupts(uword interrupt_bits) {
   ASSERT((interrupt_bits & ~kInterruptsMask) == 0);  // Must fit in mask.
 
-  // 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;
+  uword old_limit = stack_limit_.load();
+  uword new_limit;
+  do {
+    if (IsInterruptLimit(old_limit)) {
+      new_limit = old_limit | interrupt_bits;
+    } else {
+      new_limit = (kInterruptStackLimit & ~kInterruptsMask) | interrupt_bits;
     }
-  }
-
-  if (stack_limit_ == saved_stack_limit_) {
-    stack_limit_ = (kInterruptStackLimit & ~kInterruptsMask) | interrupt_bits;
-  } else {
-    stack_limit_ = stack_limit_ | interrupt_bits;
-  }
+  } while (!stack_limit_.compare_exchange_weak(old_limit, new_limit));
 }
 
 uword Thread::GetAndClearInterrupts() {
-  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_;
+  uword interrupt_bits = 0;
+  uword old_limit = stack_limit_.load();
+  uword new_limit = saved_stack_limit_;
+  do {
+    if (IsInterruptLimit(old_limit)) {
+      interrupt_bits = interrupt_bits | (old_limit & kInterruptsMask);
+    } else {
+      return interrupt_bits;
+    }
+  } while (!stack_limit_.compare_exchange_weak(old_limit, new_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 2d80830..6e3ff67 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -417,11 +417,10 @@
   };
 
   void ScheduleInterrupts(uword interrupt_bits);
-  void ScheduleInterruptsLocked(uword interrupt_bits);
   ErrorPtr HandleInterrupts();
   uword GetAndClearInterrupts();
   bool HasScheduledInterrupts() const {
-    return (stack_limit_ & kInterruptsMask) != 0;
+    return (stack_limit_.load() & kInterruptsMask) != 0;
   }
 
   // Monitor corresponding to this thread.
@@ -1031,7 +1030,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.
-  RelaxedAtomic<uword> stack_limit_;
+  volatile RelaxedAtomic<uword> stack_limit_;
   uword write_barrier_mask_;
   uword heap_base_;
   Isolate* isolate_;
@@ -1107,9 +1106,6 @@
   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;
 
@@ -1205,9 +1201,6 @@
 
   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)
@@ -1218,9 +1211,7 @@
   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
new file mode 100644
index 0000000..321fbda4
--- /dev/null
+++ b/tests/lib/isolate/kill_infinite_loop_in_initializer_test.dart
@@ -0,0 +1,44 @@
+// 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
new file mode 100644
index 0000000..19cf26b
--- /dev/null
+++ b/tests/lib_2/isolate/kill_infinite_loop_in_initializer_test.dart
@@ -0,0 +1,46 @@
+// 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.
+
+// @dart = 2.9
+
+// 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);
+  });
+}
diff --git a/tools/VERSION b/tools/VERSION
index 03401a3..d854130 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 313
+PRERELEASE 314
 PRERELEASE_PATCH 0
\ No newline at end of file