diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index 7251d81bb2b..e171a920401 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -267,6 +267,17 @@ DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 5) { } +DEFINE_NATIVE_ENTRY(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0) { + const Array& result = Array::Handle(Array::New(3)); + result.SetAt(0, SendPort::Handle(SendPort::New(isolate->main_port()))); + result.SetAt(1, Capability::Handle( + Capability::New(isolate->pause_capability()))); + result.SetAt(2, Capability::Handle( + Capability::New(isolate->terminate_capability()))); + return result.raw(); +} + + DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 2) { GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(Array, msg, arguments->NativeArgAt(1)); diff --git a/runtime/lib/isolate_patch.dart b/runtime/lib/isolate_patch.dart index 5c3d966daff..8646cb8d110 100644 --- a/runtime/lib/isolate_patch.dart +++ b/runtime/lib/isolate_patch.dart @@ -256,6 +256,10 @@ void _startIsolate(SendPort parentPort, } patch class Isolate { + static final _currentIsolate = _getCurrentIsolate(); + + /* patch */ static Isolate get current => _currentIsolate; + /* patch */ static Future spawn( void entryPoint(message), var message, { bool paused: false }) { // `paused` isn't handled yet. @@ -392,4 +396,14 @@ patch class Isolate { /* patch */ void removeErrorListener(SendPort port) { throw new UnsupportedError("removeErrorListener"); } + + static Isolate _getCurrentIsolate() { + List portAndCapabilities = _getPortAndCapabilitiesOfCurrentIsolate(); + return new Isolate(portAndCapabilities[0], + pauseCapability: portAndCapabilities[1], + terminateCapability: portAndCapabilities[2]); + } + + static List _getPortAndCapabilitiesOfCurrentIsolate() + native "Isolate_getPortAndCapabilitiesOfCurrentIsolate"; } diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index b606d39d452..2f65c7161dc 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -294,6 +294,7 @@ namespace dart { V(Int32x4_select, 3) \ V(Isolate_spawnFunction, 3) \ V(Isolate_spawnUri, 5) \ + V(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0) \ V(Isolate_sendOOB, 2) \ V(Mirrors_evalInLibraryWithPrivateKey, 2) \ V(Mirrors_makeLocalClassMirror, 1) \ diff --git a/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart b/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart index 3d16eeb59ca..9936e00fbac 100644 --- a/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart +++ b/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart @@ -1035,6 +1035,13 @@ class IsolateNatives { }, 'nonworker start'); } + static Isolate get currentIsolate { + _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); + return new Isolate(context.controlPort.sendPort, + pauseCapability: context.pauseCapability, + terminateCapability: context.terminateCapability); + } + static void _startIsolate(Function topLevel, List args, message, bool isSpawnUri, diff --git a/sdk/lib/_internal/compiler/js_lib/isolate_patch.dart b/sdk/lib/_internal/compiler/js_lib/isolate_patch.dart index cbba9e54b77..b612cced569 100644 --- a/sdk/lib/_internal/compiler/js_lib/isolate_patch.dart +++ b/sdk/lib/_internal/compiler/js_lib/isolate_patch.dart @@ -14,6 +14,13 @@ import 'dart:_isolate_helper' show CapabilityImpl, @patch class Isolate { + static final _currentIsolateCache = IsolateNatives.currentIsolate; + + // `current` must be a getter, not just a final field, + // to match the external declaration. + @patch + static Isolate get current => _currentIsolateCache; + @patch static Future spawn(void entryPoint(message), var message, { bool paused: false }) { diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart index 6a2bfb43eac..35d2a8b15c9 100644 --- a/sdk/lib/isolate/isolate.dart +++ b/sdk/lib/isolate/isolate.dart @@ -119,6 +119,15 @@ class Isolate { Isolate(this.controlPort, {this.pauseCapability, this.terminateCapability}); + /** + * Return the current [Isolate]. + * + * The isolate gives access to the capabilities needed to inspect, + * pause or kill the isolate, and allows granting these capabilities + * to others. + */ + external static Isolate get current; + /** * Creates and spawns an isolate that shares the same code as the current * isolate. diff --git a/tests/isolate/isolate_current_test.dart b/tests/isolate/isolate_current_test.dart new file mode 100644 index 00000000000..671dca44fa6 --- /dev/null +++ b/tests/isolate/isolate_current_test.dart @@ -0,0 +1,135 @@ +// Copyright (c) 2014, 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. + +library isolate_current_test; +import "dart:isolate"; +import "dart:async"; +import "package:expect/expect.dart"; +import "package:async_helper/async_helper.dart"; + +void main() { + asyncStart(); + + Expect.isNotNull(Isolate.current); + + // Sending controlPort and capabilities as list. + testSend(i2l, l2i); + testSpawnReturnVsCurrent(true); + testSpawnReturnVsCurrent2(true); + + // Sending Isolate itself. + testSend(id, id); + testSpawnReturnVsCurrent(false); + testSpawnReturnVsCurrent2(false); + + asyncEnd(); +} + +/** Test sending the isolate data or isolate through a [SendPort]. */ +void testSend(i2l, l2i) { + asyncStart(); + RawReceivePort p = new RawReceivePort(); + Isolate isolate = Isolate.current; + p.handler = (list) { + var isolate2 = l2i(list); + Expect.equals(isolate.controlPort, isolate2.controlPort); + Expect.equals(isolate.pauseCapability, isolate2.pauseCapability); + Expect.equals(isolate.terminateCapability, isolate2.terminateCapability); + p.close(); + asyncEnd(); + }; + p.sendPort.send(i2l(isolate)); +} + +/** + * Test that the isolate returned by [Isolate.spawn] is the same as + * the one returned by [Isolate.current] in the spawned isolate. + * Checked in the spawning isolate. + */ +void testSpawnReturnVsCurrent(bool asList) { + asyncStart(); + Function transform = asList ? l2i : id; + Completer response = new Completer(); + var p = new RawReceivePort(); + p.handler = (v) { + response.complete(transform(v)); + p.close(); + }; + + Isolate.spawn(replyCurrent, [p.sendPort, asList]).then((Isolate isolate) { + return response.future.then((Isolate isolate2) { + expectIsolateEquals(isolate, isolate2); + asyncEnd(); + }); + }); +} + +void replyCurrent(args) { + SendPort responsePort = args[0]; + Function transform = args[1] ? i2l : id; + responsePort.send(transform(Isolate.current)); +} + +/** + * Test that the isolate returned by [Isolate.spawn] is the same as + * the one returned by [Isolate.current] in the spawned isolate. + * Checked in the spawned isolate. + */ +void testSpawnReturnVsCurrent2(bool asList) { + asyncStart(); + Function transform = asList ? i2l : id; + + Completer response = new Completer(); + var p = new RawReceivePort(); + int state = 0; + p.handler = (v) { + switch (state) { + case 0: + response.complete(v); + state++; + break; + case 1: + p.close(); + Expect.isTrue(v); + asyncEnd(); + } + }; + + Isolate.spawn(expectCurrent, [p.sendPort, asList]).then((Isolate isolate) { + return response.future.then((SendPort port) { + port.send(transform(isolate)); + }); + }); +} + +void expectCurrent(args) { + SendPort responsePort = args[0]; + Function transform = args[1] ? l2i : id; + RawReceivePort port = new RawReceivePort(); + port.handler = (isoData) { + Isolate isolate2 = transform(isoData); + port.close(); + Isolate isolate = Isolate.current; + expectIsolateEquals(isolate, isolate2); + responsePort.send(true); + }; + responsePort.send(port.sendPort); +} + +/** Convert isolate to list (of control port and capabilities). */ +i2l(Isolate isolate) => [isolate.controlPort, + isolate.pauseCapability, + isolate.terminateCapability]; +/** Convert list to isolate. */ +l2i(List list) => new Isolate(list[0], pauseCapability: list[1], + terminateCapability: list[2]); + +/** Identity transformation. */ +id(Isolate isolate) => isolate; + +void expectIsolateEquals(Isolate expect, Isolate actual) { + Expect.equals(expect.controlPort, actual.controlPort); + Expect.equals(expect.pauseCapability, actual.pauseCapability); + Expect.equals(expect.terminateCapability, actual.terminateCapability); +}