[io] Exit the isolate during Process.runSync and sleep.

This prevents such an isolate from occupying one of the limited number of mutator slots and blocking other isolates in the same group from running.

TEST=ci
Bug: https://github.com/dart-lang/sdk/issues/51254
Bug: https://github.com/dart-lang/sdk/issues/54687
Bug: https://github.com/dart-lang/sdk/issues/57119
Change-Id: Ic04bbaa7f482d533ad0ecf2c6da17ea9f00c264e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/398927
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 2287790..b414d73 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -651,6 +651,20 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedBlockingCall);
 };
 
+// Remove once we remove the limitation on the number of running mutators.
+// https://github.com/dart-lang/sdk/issues/54687
+class LeaveIsolateScope {
+ public:
+  LeaveIsolateScope() : isolate_(Dart_CurrentIsolate()) { Dart_ExitIsolate(); }
+  ~LeaveIsolateScope() { Dart_EnterIsolate(isolate_); }
+
+ private:
+  Dart_Isolate isolate_;
+
+  DISALLOW_ALLOCATION();
+  DISALLOW_COPY_AND_ASSIGN(LeaveIsolateScope);
+};
+
 struct MagicNumberData {
   static constexpr intptr_t kMaxLength = 8;
 
diff --git a/runtime/bin/process.cc b/runtime/bin/process.cc
index 309f690..f990e80 100644
--- a/runtime/bin/process.cc
+++ b/runtime/bin/process.cc
@@ -285,6 +285,8 @@
   int64_t milliseconds = 0;
   // Ignore result if passing invalid argument and just set exit code to 0.
   DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &milliseconds);
+
+  LeaveIsolateScope leave_isolate;
   TimerUtils::Sleep(milliseconds);
 }
 
diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc
index 5439d4e..64e74fc 100644
--- a/runtime/bin/process_linux.cc
+++ b/runtime/bin/process_linux.cc
@@ -849,6 +849,8 @@
 
   int alive = 3;
   while (alive > 0) {
+    LeaveIsolateScope leave_isolate;
+
     // Blocking call waiting for events from the child process.
     if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) {
       return CloseProcessBuffers(fds, alive);
diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc
index 24300ee..d7eb45e 100644
--- a/runtime/bin/process_macos.cc
+++ b/runtime/bin/process_macos.cc
@@ -826,6 +826,8 @@
 
   int alive = 3;
   while (alive > 0) {
+    LeaveIsolateScope leave_isolate;
+
     // Blocking call waiting for events from the child process.
     if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) {
       return CloseProcessBuffers(fds, alive);
diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc
index 44b61e0..e9a353b 100644
--- a/runtime/bin/process_win.cc
+++ b/runtime/bin/process_win.cc
@@ -828,6 +828,8 @@
   // Continue until all handles are closed.
   int alive = kHandles;
   while (alive > 0) {
+    LeaveIsolateScope leave_isolate;
+
     // Blocking call waiting for events from the child process.
     DWORD wait_result = WaitForMultipleObjects(alive, events, FALSE, INFINITE);
 
diff --git a/runtime/tests/vm/dart/isolates/many_isolates_blocked_at_process_run_sync_test.dart b/runtime/tests/vm/dart/isolates/many_isolates_blocked_at_process_run_sync_test.dart
new file mode 100644
index 0000000..42d4d9a
--- /dev/null
+++ b/runtime/tests/vm/dart/isolates/many_isolates_blocked_at_process_run_sync_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2024, 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.
+
+import "dart:isolate";
+import "dart:io";
+
+child(replyPort) {
+  replyPort.send(null);
+  Process.runSync("sleep", ["3600"]);
+}
+
+main() async {
+  var pending = 0;
+  var port;
+  port = new RawReceivePort((msg) {
+    pending--;
+    if (pending == 0) exit(0);
+  });
+
+  for (var i = 0; i < 20; i++) {
+    Isolate.spawn(child, port.sendPort);
+    pending++;
+  }
+}
diff --git a/runtime/tests/vm/dart/isolates/many_isolates_blocked_at_sleep_test.dart b/runtime/tests/vm/dart/isolates/many_isolates_blocked_at_sleep_test.dart
new file mode 100644
index 0000000..d053d68
--- /dev/null
+++ b/runtime/tests/vm/dart/isolates/many_isolates_blocked_at_sleep_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2024, 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.
+
+import "dart:isolate";
+import "dart:io";
+
+child(replyPort) {
+  replyPort.send(null);
+  sleep(Duration(minutes: 60));
+}
+
+main() async {
+  var pending = 0;
+  var port;
+  port = new RawReceivePort((msg) {
+    pending--;
+    if (pending == 0) exit(0);
+  });
+
+  for (var i = 0; i < 20; i++) {
+    Isolate.spawn(child, port.sendPort);
+    pending++;
+  }
+}