diff --git a/benchmarks/IsolateJson/dart/IsolateJson.dart b/benchmarks/IsolateJson/dart/IsolateJson.dart index 5b5a9ee7d80..9717f34f7ce 100644 --- a/benchmarks/IsolateJson/dart/IsolateJson.dart +++ b/benchmarks/IsolateJson/dart/IsolateJson.dart @@ -10,8 +10,6 @@ import 'dart:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart' show BenchmarkBase; -import 'runtime/tests/vm/dart/export_sendAndExit_helper.dart' show sendAndExit; - class JsonDecodingBenchmark { JsonDecodingBenchmark(this.name, {required this.sample, @@ -80,7 +78,7 @@ Future decodeJson(bool useSendAndExit, Uint8List encodedJson) async { Future jsonDecodingIsolate(JsonDecodeRequest request) async { final result = json.decode(utf8.decode(request.encodedJson)); if (request.useSendAndExit) { - sendAndExit(request.sendPort, result); + Isolate.exit(request.sendPort, result); } else { request.sendPort.send(result); } diff --git a/benchmarks/IsolateJson/dart/runtime/tests/vm/dart/export_sendAndExit_helper.dart b/benchmarks/IsolateJson/dart/runtime/tests/vm/dart/export_sendAndExit_helper.dart deleted file mode 100644 index 75628d69d78..00000000000 --- a/benchmarks/IsolateJson/dart/runtime/tests/vm/dart/export_sendAndExit_helper.dart +++ /dev/null @@ -1 +0,0 @@ -export 'dart:_internal' show sendAndExit; diff --git a/benchmarks/IsolateJson/dart2/IsolateJson.dart b/benchmarks/IsolateJson/dart2/IsolateJson.dart index 46ac6ca3876..a78b95dd62a 100644 --- a/benchmarks/IsolateJson/dart2/IsolateJson.dart +++ b/benchmarks/IsolateJson/dart2/IsolateJson.dart @@ -13,8 +13,6 @@ import 'dart:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart' show BenchmarkBase; import 'package:meta/meta.dart'; -import 'runtime/tests/vm/dart/export_sendAndExit_helper.dart' show sendAndExit; - class JsonDecodingBenchmark { JsonDecodingBenchmark(this.name, {@required this.sample, @@ -83,7 +81,7 @@ Future decodeJson(bool useSendAndExit, Uint8List encodedJson) async { Future jsonDecodingIsolate(JsonDecodeRequest request) async { final result = json.decode(utf8.decode(request.encodedJson)); if (request.useSendAndExit) { - sendAndExit(request.sendPort, result); + Isolate.exit(request.sendPort, result); } else { request.sendPort.send(result); } diff --git a/benchmarks/IsolateJson/dart2/runtime/tests/vm/dart/export_sendAndExit_helper.dart b/benchmarks/IsolateJson/dart2/runtime/tests/vm/dart/export_sendAndExit_helper.dart deleted file mode 100644 index 75628d69d78..00000000000 --- a/benchmarks/IsolateJson/dart2/runtime/tests/vm/dart/export_sendAndExit_helper.dart +++ /dev/null @@ -1 +0,0 @@ -export 'dart:_internal' show sendAndExit; diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index f8c0bb54de7..0515336ce51 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -272,36 +272,37 @@ static ObjectPtr ValidateMessageObject(Zone* zone, return obj.ptr(); } -DEFINE_NATIVE_ENTRY(SendPortImpl_sendAndExitInternal_, 0, 2) { - GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); - if (!PortMap::IsReceiverInThisIsolateGroup(port.Id(), isolate->group())) { - const auto& error = - String::Handle(String::New("sendAndExit is only supported across " - "isolates spawned via spawnFunction.")); - Exceptions::ThrowArgumentError(error); - UNREACHABLE(); - } +DEFINE_NATIVE_ENTRY(Isolate_exit_, 0, 2) { + GET_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); + if (!port.IsNull()) { + GET_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1)); + if (!PortMap::IsReceiverInThisIsolateGroup(port.Id(), isolate->group())) { + const auto& error = + String::Handle(String::New("exit with final message is only allowed " + "for isolates in one isolate group.")); + Exceptions::ThrowArgumentError(error); + UNREACHABLE(); + } - GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1)); - - Object& validated_result = Object::Handle(zone); - const Object& msg_obj = Object::Handle(zone, obj.ptr()); - validated_result = ValidateMessageObject(zone, isolate, msg_obj); - // msg_array = [ - // , - // , - // , - // ] - const Array& msg_array = Array::Handle(zone, Array::New(3)); - msg_array.SetAt(0, msg_obj); - if (validated_result.IsUnhandledException()) { - Exceptions::PropagateError(Error::Cast(validated_result)); - UNREACHABLE(); + Object& validated_result = Object::Handle(zone); + const Object& msg_obj = Object::Handle(zone, obj.ptr()); + validated_result = ValidateMessageObject(zone, isolate, msg_obj); + // msg_array = [ + // , + // , + // , + // ] + const Array& msg_array = Array::Handle(zone, Array::New(3)); + msg_array.SetAt(0, msg_obj); + if (validated_result.IsUnhandledException()) { + Exceptions::PropagateError(Error::Cast(validated_result)); + UNREACHABLE(); + } + PersistentHandle* handle = + isolate->group()->api_state()->AllocatePersistentHandle(); + handle->set_ptr(msg_array); + isolate->bequeath(std::unique_ptr(new Bequest(handle, port.Id()))); } - PersistentHandle* handle = - isolate->group()->api_state()->AllocatePersistentHandle(); - handle->set_ptr(msg_array); - isolate->bequeath(std::unique_ptr(new Bequest(handle, port.Id()))); Isolate::KillIfExists(isolate, Isolate::LibMsgId::kKillMsg); // Drain interrupts before running so any IMMEDIATE operations on the current // isolate happen synchronously. diff --git a/runtime/tests/vm/dart/isolates/internal.dart b/runtime/tests/vm/dart/isolates/internal.dart deleted file mode 100644 index ec854b0b9ec..00000000000 --- a/runtime/tests/vm/dart/isolates/internal.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2020, 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:io'; -import 'dart:isolate'; -import 'dart:async'; -import 'dart:_internal' as dart_internal; - -extension SendPortSendAndExit on SendPort { - void sendAndExit(var message) { - dart_internal.sendAndExit(this, message); - } -} diff --git a/runtime/tests/vm/dart/isolates/ring_gc_sendAndExit_test.dart b/runtime/tests/vm/dart/isolates/ring_gc_sendAndExit_test.dart index 74b682039f2..a50d9e82ec9 100644 --- a/runtime/tests/vm/dart/isolates/ring_gc_sendAndExit_test.dart +++ b/runtime/tests/vm/dart/isolates/ring_gc_sendAndExit_test.dart @@ -24,7 +24,7 @@ main(args) async { final ring = await Ring.create(numIsolates); // Let each node produce a tree, send it to it's neighbour and let it return - // the one it received (via sendAndExit). + // the one it received (via Isolate.exit). final results = await ring.runAndClose((int id) => Worker(id)); Expect.equals(numIsolates, results.length); diff --git a/runtime/tests/vm/dart/isolates/test_utils.dart b/runtime/tests/vm/dart/isolates/test_utils.dart index 7c5da815e73..3268cc18a2e 100644 --- a/runtime/tests/vm/dart/isolates/test_utils.dart +++ b/runtime/tests/vm/dart/isolates/test_utils.dart @@ -6,8 +6,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:isolate'; -import 'internal.dart'; - export '../../../../../benchmarks/IsolateFibonacci/dart/IsolateFibonacci.dart' show fibonacciRecursive; @@ -102,7 +100,7 @@ class Ring { case Command.kRunAndClose: final RingElement re = args[1]; final SendPort nextNeighbor = args[2]; - port.sendAndExit(await re.run(nextNeighbor, siData)); + Isolate.exit(port, await re.run(nextNeighbor, siData)); break; case Command.kClose: port.send('done'); diff --git a/runtime/tests/vm/dart/sendandexit_test.dart b/runtime/tests/vm/dart/sendandexit_test.dart index 58e02c62889..235a3c2e9b4 100644 --- a/runtime/tests/vm/dart/sendandexit_test.dart +++ b/runtime/tests/vm/dart/sendandexit_test.dart @@ -4,9 +4,8 @@ // // VMOptions=--enable-isolate-groups // -// Validates functionality of sendAndExit. +// Validates functionality of Isolate.exit(). -import 'dart:_internal' show sendAndExit; import 'dart:async'; import 'dart:isolate'; import 'dart:nativewrappers'; @@ -27,7 +26,7 @@ spawnWorker(worker, data) async { verifyCantSendAnonymousClosure() async { final receivePort = ReceivePort(); Expect.throws( - () => sendAndExit(receivePort.sendPort, () {}), + () => Isolate.exit(receivePort.sendPort, () {}), (e) => e.toString() == 'Invalid argument: "Illegal argument in isolate message : ' @@ -40,7 +39,7 @@ class NativeWrapperClass extends NativeFieldWrapperClass1 {} verifyCantSendNative() async { final receivePort = ReceivePort(); Expect.throws( - () => sendAndExit(receivePort.sendPort, NativeWrapperClass()), + () => Isolate.exit(receivePort.sendPort, NativeWrapperClass()), (e) => e.toString().startsWith('Invalid argument: ' '"Illegal argument in isolate message : ' '(object extends NativeWrapper')); @@ -50,7 +49,7 @@ verifyCantSendNative() async { verifyCantSendReceivePort() async { final receivePort = ReceivePort(); Expect.throws( - () => sendAndExit(receivePort.sendPort, receivePort), + () => Isolate.exit(receivePort.sendPort, receivePort), // closure is encountered first before we reach ReceivePort instance (e) => e.toString().startsWith( 'Invalid argument: "Illegal argument in isolate message : ' @@ -62,7 +61,7 @@ verifyCantSendRegexp() async { final receivePort = ReceivePort(); final regexp = RegExp(""); Expect.throws( - () => sendAndExit(receivePort.sendPort, regexp), + () => Isolate.exit(receivePort.sendPort, regexp), (e) => e.toString() == 'Invalid argument: ' @@ -73,7 +72,7 @@ verifyCantSendRegexp() async { add(a, b) => a + b; worker(SendPort sendPort) async { - sendAndExit(sendPort, add); + Isolate.exit(sendPort, add); } verifyCanSendStaticMethod() async { diff --git a/runtime/tests/vm/dart_2/isolates/internal.dart b/runtime/tests/vm/dart_2/isolates/internal.dart deleted file mode 100644 index 1742a43a66d..00000000000 --- a/runtime/tests/vm/dart_2/isolates/internal.dart +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2020, 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 - -import 'dart:io'; -import 'dart:isolate'; -import 'dart:async'; -import 'dart:_internal' as dart_internal; - -extension SendPortSendAndExit on SendPort { - void sendAndExit(var message) { - dart_internal.sendAndExit(this, message); - } -} diff --git a/runtime/tests/vm/dart_2/isolates/ring_gc_sendAndExit_test.dart b/runtime/tests/vm/dart_2/isolates/ring_gc_sendAndExit_test.dart index 56b636c0c28..2c7011cb5e0 100644 --- a/runtime/tests/vm/dart_2/isolates/ring_gc_sendAndExit_test.dart +++ b/runtime/tests/vm/dart_2/isolates/ring_gc_sendAndExit_test.dart @@ -26,7 +26,7 @@ main(args) async { final ring = await Ring.create(numIsolates); // Let each node produce a tree, send it to it's neighbour and let it return - // the one it received (via sendAndExit). + // the one it received (via Isolate.exit()). final results = await ring.runAndClose((int id) => Worker(id)); Expect.equals(numIsolates, results.length); diff --git a/runtime/tests/vm/dart_2/isolates/test_utils.dart b/runtime/tests/vm/dart_2/isolates/test_utils.dart index 0c93f3dfc84..a57bb41fb86 100644 --- a/runtime/tests/vm/dart_2/isolates/test_utils.dart +++ b/runtime/tests/vm/dart_2/isolates/test_utils.dart @@ -8,8 +8,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:isolate'; -import 'internal.dart'; - export '../../../../../benchmarks/IsolateFibonacci/dart2/IsolateFibonacci.dart' show fibonacciRecursive; @@ -104,7 +102,7 @@ class Ring { case Command.kRunAndClose: final RingElement re = args[1]; final SendPort nextNeighbor = args[2]; - port.sendAndExit(await re.run(nextNeighbor, siData)); + Isolate.exit(port, await re.run(nextNeighbor, siData)); break; case Command.kClose: port.send('done'); diff --git a/runtime/tests/vm/dart_2/sendandexit_test.dart b/runtime/tests/vm/dart_2/sendandexit_test.dart index f64b1ee379b..3ee64fe40f3 100644 --- a/runtime/tests/vm/dart_2/sendandexit_test.dart +++ b/runtime/tests/vm/dart_2/sendandexit_test.dart @@ -4,11 +4,10 @@ // // VMOptions=--enable-isolate-groups // -// Validates functionality of sendAndExit. +// Validates functionality of Isolate.exit(). // @dart = 2.9 -import 'dart:_internal' show sendAndExit; import 'dart:async'; import 'dart:isolate'; import 'dart:nativewrappers'; @@ -29,7 +28,7 @@ spawnWorker(worker, data) async { verifyCantSendAnonymousClosure() async { final receivePort = ReceivePort(); Expect.throws( - () => sendAndExit(receivePort.sendPort, () {}), + () => Isolate.exit(receivePort.sendPort, () {}), (e) => e.toString() == 'Invalid argument: "Illegal argument in isolate message : ' @@ -42,7 +41,7 @@ class NativeWrapperClass extends NativeFieldWrapperClass1 {} verifyCantSendNative() async { final receivePort = ReceivePort(); Expect.throws( - () => sendAndExit(receivePort.sendPort, NativeWrapperClass()), + () => Isolate.exit(receivePort.sendPort, NativeWrapperClass()), (e) => e.toString().startsWith('Invalid argument: ' '"Illegal argument in isolate message : ' '(object extends NativeWrapper')); @@ -52,7 +51,7 @@ verifyCantSendNative() async { verifyCantSendReceivePort() async { final receivePort = ReceivePort(); Expect.throws( - () => sendAndExit(receivePort.sendPort, receivePort), + () => Isolate.exit(receivePort.sendPort, receivePort), // closure is encountered first before we reach ReceivePort instance (e) => e.toString().startsWith( 'Invalid argument: "Illegal argument in isolate message : ' @@ -64,7 +63,7 @@ verifyCantSendRegexp() async { final receivePort = ReceivePort(); final regexp = RegExp(""); Expect.throws( - () => sendAndExit(receivePort.sendPort, regexp), + () => Isolate.exit(receivePort.sendPort, regexp), (e) => e.toString() == 'Invalid argument: ' @@ -75,7 +74,7 @@ verifyCantSendRegexp() async { add(a, b) => a + b; worker(SendPort sendPort) async { - sendAndExit(sendPort, add); + Isolate.exit(sendPort, add); } verifyCanSendStaticMethod() async { diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 4911d25fc68..6f6d67d3df1 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -65,7 +65,6 @@ namespace dart { V(SendPortImpl_get_id, 1) \ V(SendPortImpl_get_hashcode, 1) \ V(SendPortImpl_sendInternal_, 2) \ - V(SendPortImpl_sendAndExitInternal_, 2) \ V(Smi_bitNegate, 1) \ V(Smi_bitLength, 1) \ V(Mint_bitNegate, 1) \ @@ -316,12 +315,13 @@ namespace dart { V(Int32x4_setFlagZ, 2) \ V(Int32x4_setFlagW, 2) \ V(Int32x4_select, 3) \ + V(Isolate_exit_, 2) \ + V(Isolate_getCurrentRootUriStr, 0) \ + V(Isolate_getDebugName, 1) \ + V(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0) \ + V(Isolate_sendOOB, 2) \ V(Isolate_spawnFunction, 10) \ V(Isolate_spawnUri, 12) \ - V(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0) \ - V(Isolate_getCurrentRootUriStr, 0) \ - V(Isolate_sendOOB, 2) \ - V(Isolate_getDebugName, 1) \ V(GrowableList_allocate, 2) \ V(GrowableList_getIndexed, 2) \ V(GrowableList_setIndexed, 3) \ diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc index 863880e03d8..bed2681d9b4 100644 --- a/runtime/vm/dart_api_impl_test.cc +++ b/runtime/vm/dart_api_impl_test.cc @@ -756,11 +756,10 @@ void Func1() { TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenSendAndExit) { const char* kScriptChars = R"( import 'dart:isolate'; -import 'dart:_internal' show sendAndExit; sendAndExitNow() { final receivePort = ReceivePort(); - sendAndExit(receivePort.sendPort, true); + Isolate.exit(receivePort.sendPort, true); } @pragma("vm:external-name", "Test_nativeFunc") diff --git a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart index 67993478679..0d123119659 100644 --- a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart +++ b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart @@ -77,6 +77,10 @@ class Isolate { @patch void removeErrorListener(SendPort port) => _unsupported(); + + @patch + static Never exit([SendPort? finalMessagePort, Object? message]) => + _unsupported(); } /** Default factory for receive ports. */ diff --git a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart index 1c0b0537600..2bb2fa13662 100644 --- a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart @@ -106,6 +106,11 @@ class Isolate { void removeErrorListener(SendPort port) { throw new UnsupportedError("Isolate.removeErrorListener"); } + + @patch + static Never exit([SendPort? finalMessagePort, Object? message]) { + throw new UnsupportedError("Isolate.exit"); + } } @patch diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart index 421fb4d77be..569b91cc51d 100644 --- a/sdk/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk/lib/_internal/vm/lib/internal_patch.dart @@ -178,9 +178,6 @@ external void reachabilityFence(Object object); @pragma("vm:external-name", "Internal_nativeEffect") external void _nativeEffect(Object object); -@pragma("vm:external-name", "SendPortImpl_sendAndExitInternal_") -external void sendAndExit(SendPort sendPort, var message); - // Collection of functions which should only be used for testing purposes. abstract class VMInternalsForTesting { // This function can be used by tests to enforce garbage collection. diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart index b7bf498e6cc..37fb3909061 100644 --- a/sdk/lib/_internal/vm/lib/isolate_patch.dart +++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart @@ -645,6 +645,13 @@ class Isolate { @pragma("vm:external-name", "Isolate_getCurrentRootUriStr") external static String _getCurrentRootUriStr(); + + @pragma("vm:external-name", "Isolate_exit_") + external static Never _exit(SendPort? finalMessagePort, Object? message); + + static Never exit([SendPort? finalMessagePort, Object? message]) { + _exit(finalMessagePort, message); + } } @patch diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart index 8a68d1005a5..fc47a290af6 100644 --- a/sdk/lib/isolate/isolate.dart +++ b/sdk/lib/isolate/isolate.dart @@ -553,6 +553,32 @@ class Isolate { }; return controller.stream; } + + /// Terminates the current isolate synchronously. + /// + /// This operations is potentially dangerous and should be used judiciously. + /// The isolate stops operating *immediately*. It throws if optional [message] + /// does not adhere to the limitation on what can be send from one isolate to + /// another. It also throws if a [finalMessagePort] is associated with an + /// isolate spawned outside of current isolate group, spawned via [spawnUri]. + /// + /// If successful, a call to this method does not return. Pending `finally` + /// blocks are not executed, control flow will not go back to the event loop, + /// scheduled asynchronous asks will never run, and even pending isolate + /// control commands may be ignored. (The isolate will send messages to ports + /// already registered using [Isolate.addOnExitListener], but no further Dart + /// code will run in the isolate.) + /// + /// If [finalMessagePort] is provided, and the [message] can be sent through + /// it, then the message is sent through that port as the final operation of + /// the current isolate. The isolate terminates immediately after + /// that [SendPort.send] call returns. + /// + /// (If the port is a native port, one provided by [ReceivePort.sendPort] + /// or [RawReceivePort.sendPort], the system may be able to send this final + /// message more efficiently than normal port communication between live + /// isolates.) + external static Never exit([SendPort? finalMessagePort, Object? message]); } /// Sends messages to its [ReceivePort]s. diff --git a/tests/lib/mirrors/invocation_fuzz_test.dart b/tests/lib/mirrors/invocation_fuzz_test.dart index ea8f546a027..ed8ba09a012 100644 --- a/tests/lib/mirrors/invocation_fuzz_test.dart +++ b/tests/lib/mirrors/invocation_fuzz_test.dart @@ -20,6 +20,7 @@ var denylist = [ // Don't exit the test pre-maturely. 'dart.io.exit', + 'dart.isolate.Isolate.exit', // Don't change the exit code, which may fool the test harness. 'dart.io.exitCode', diff --git a/tests/lib_2/mirrors/invocation_fuzz_test.dart b/tests/lib_2/mirrors/invocation_fuzz_test.dart index 63238065d38..68360d11abf 100644 --- a/tests/lib_2/mirrors/invocation_fuzz_test.dart +++ b/tests/lib_2/mirrors/invocation_fuzz_test.dart @@ -22,6 +22,7 @@ var denylist = [ // Don't exit the test pre-maturely. 'dart.io.exit', + 'dart.isolate.Isolate.exit', // Don't change the exit code, which may fool the test harness. 'dart.io.exitCode',