mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
Add 'startPaused' option to Isolate.spawn*.
Initial implementation of startPaused in dart2js. R=floitsch@google.com Review URL: https://codereview.chromium.org//163523003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@32722 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
38e5d47786
commit
b23f5d0327
|
@ -227,7 +227,8 @@ void _startIsolate(Function entryPoint, bool isSpawnUri) {
|
|||
|
||||
patch class Isolate {
|
||||
/* patch */ static Future<Isolate> spawn(
|
||||
void entryPoint(message), var message) {
|
||||
void entryPoint(message), var message, { bool paused: false }) {
|
||||
// `paused` isn't handled yet.
|
||||
try {
|
||||
// The VM will invoke [_startIsolate] with entryPoint as argument.
|
||||
SendPort controlPort = _spawnFunction(entryPoint);
|
||||
|
@ -246,7 +247,8 @@ patch class Isolate {
|
|||
}
|
||||
|
||||
/* patch */ static Future<Isolate> spawnUri(
|
||||
Uri uri, List<String> args, var message) {
|
||||
Uri uri, List<String> args, var message, { bool paused: false }) {
|
||||
// `paused` isn't handled yet.
|
||||
try {
|
||||
// The VM will invoke [_startIsolate] and not `main`.
|
||||
SendPort controlPort = _spawnUri(uri.toString());
|
||||
|
|
|
@ -264,7 +264,7 @@ class _IsolateContext implements IsolateContext {
|
|||
|
||||
final Capability pauseCapability = new Capability();
|
||||
|
||||
// TODO(lrn): Store these in single "pausestate" object, so they don't take
|
||||
// TODO(lrn): Store these in single "PauseState" object, so they don't take
|
||||
// up as much room when not pausing.
|
||||
bool isPaused = false;
|
||||
List<_IsolateEvent> delayedEvents = [];
|
||||
|
@ -279,6 +279,7 @@ class _IsolateContext implements IsolateContext {
|
|||
if (pauseTokens.add(resume) && !isPaused) {
|
||||
isPaused = true;
|
||||
}
|
||||
_updateGlobalState();
|
||||
}
|
||||
|
||||
void removePause(Capability resume) {
|
||||
|
@ -291,6 +292,7 @@ class _IsolateContext implements IsolateContext {
|
|||
}
|
||||
isPaused = false;
|
||||
}
|
||||
_updateGlobalState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -354,7 +356,7 @@ class _IsolateContext implements IsolateContext {
|
|||
}
|
||||
|
||||
_updateGlobalState() {
|
||||
if (ports.length - weakPorts.length > 0) {
|
||||
if (ports.length - weakPorts.length > 0 || isPaused) {
|
||||
_globalState.isolates[id] = this; // indicate this isolate is active
|
||||
} else {
|
||||
_globalState.isolates.remove(id); // indicate this isolate is not active
|
||||
|
@ -592,10 +594,12 @@ class IsolateNatives {
|
|||
var args = msg['args'];
|
||||
var message = _deserializeMessage(msg['msg']);
|
||||
var isSpawnUri = msg['isSpawnUri'];
|
||||
var startPaused = msg['startPaused'];
|
||||
var replyTo = _deserializeMessage(msg['replyTo']);
|
||||
var context = new _IsolateContext();
|
||||
_globalState.topEventLoop.enqueue(context, () {
|
||||
_startIsolate(entryPoint, args, message, isSpawnUri, replyTo);
|
||||
_startIsolate(entryPoint, args, message,
|
||||
isSpawnUri, startPaused, replyTo);
|
||||
}, 'worker-start');
|
||||
// Make sure we always have a current context in this worker.
|
||||
// TODO(7907): This is currently needed because we're using
|
||||
|
@ -609,7 +613,8 @@ class IsolateNatives {
|
|||
case 'spawn-worker':
|
||||
_spawnWorker(msg['functionName'], msg['uri'],
|
||||
msg['args'], msg['msg'],
|
||||
msg['isSpawnUri'], msg['replyPort']);
|
||||
msg['isSpawnUri'], msg['startPaused'],
|
||||
msg['replyPort']);
|
||||
break;
|
||||
case 'message':
|
||||
SendPort port = msg['port'];
|
||||
|
@ -677,17 +682,24 @@ class IsolateNatives {
|
|||
}
|
||||
|
||||
static Future<List> spawnFunction(void topLevelFunction(message),
|
||||
message) {
|
||||
var message,
|
||||
bool startPaused) {
|
||||
final name = _getJSFunctionName(topLevelFunction);
|
||||
if (name == null) {
|
||||
throw new UnsupportedError(
|
||||
"only top-level functions can be spawned.");
|
||||
}
|
||||
return spawn(name, null, null, message, false, false);
|
||||
bool isLight = false;
|
||||
bool isSpawnUri = false;
|
||||
return spawn(name, null, null, message, isLight, isSpawnUri, startPaused);
|
||||
}
|
||||
|
||||
static Future<List> spawnUri(Uri uri, List<String> args, message) {
|
||||
return spawn(null, uri.toString(), args, message, false, true);
|
||||
static Future<List> spawnUri(Uri uri, List<String> args, var message,
|
||||
bool startPaused) {
|
||||
bool isLight = false;
|
||||
bool isSpawnUri = true;
|
||||
return spawn(null, uri.toString(), args, message,
|
||||
isLight, isSpawnUri, startPaused);
|
||||
}
|
||||
|
||||
// TODO(sigmund): clean up above, after we make the new API the default:
|
||||
|
@ -695,7 +707,7 @@ class IsolateNatives {
|
|||
/// If [uri] is `null` it is replaced with the current script.
|
||||
static Future<List> spawn(String functionName, String uri,
|
||||
List<String> args, message,
|
||||
bool isLight, bool isSpawnUri) {
|
||||
bool isLight, bool isSpawnUri, bool startPaused) {
|
||||
// Assume that the compiled version of the Dart file lives just next to the
|
||||
// dart file.
|
||||
// TODO(floitsch): support precompiled version of dart2js output.
|
||||
|
@ -710,10 +722,12 @@ class IsolateNatives {
|
|||
SendPort signalReply = port.sendPort;
|
||||
|
||||
if (_globalState.useWorkers && !isLight) {
|
||||
_startWorker(functionName, uri, args, message, isSpawnUri, signalReply);
|
||||
_startWorker(functionName, uri, args, message, isSpawnUri, startPaused,
|
||||
signalReply);
|
||||
} else {
|
||||
_startNonWorker(
|
||||
functionName, uri, args, message, isSpawnUri, signalReply);
|
||||
functionName, uri, args, message, isSpawnUri, startPaused,
|
||||
signalReply);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -722,6 +736,7 @@ class IsolateNatives {
|
|||
String functionName, String uri,
|
||||
List<String> args, message,
|
||||
bool isSpawnUri,
|
||||
bool startPaused,
|
||||
SendPort replyPort) {
|
||||
if (_globalState.isWorker) {
|
||||
_globalState.mainManager.postMessage(_serializeMessage({
|
||||
|
@ -731,16 +746,19 @@ class IsolateNatives {
|
|||
'msg': message,
|
||||
'uri': uri,
|
||||
'isSpawnUri': isSpawnUri,
|
||||
'startPaused': startPaused,
|
||||
'replyPort': replyPort}));
|
||||
} else {
|
||||
_spawnWorker(functionName, uri, args, message, isSpawnUri, replyPort);
|
||||
_spawnWorker(functionName, uri, args, message,
|
||||
isSpawnUri, startPaused, replyPort);
|
||||
}
|
||||
}
|
||||
|
||||
static void _startNonWorker(
|
||||
String functionName, String uri,
|
||||
List<String> args, message,
|
||||
List<String> args, var message,
|
||||
bool isSpawnUri,
|
||||
bool startPaused,
|
||||
SendPort replyPort) {
|
||||
// TODO(eub): support IE9 using an iframe -- Dart issue 1702.
|
||||
if (uri != null) {
|
||||
|
@ -749,13 +767,14 @@ class IsolateNatives {
|
|||
}
|
||||
_globalState.topEventLoop.enqueue(new _IsolateContext(), () {
|
||||
final func = _getJSFunctionFromName(functionName);
|
||||
_startIsolate(func, args, message, isSpawnUri, replyPort);
|
||||
_startIsolate(func, args, message, isSpawnUri, startPaused, replyPort);
|
||||
}, 'nonworker start');
|
||||
}
|
||||
|
||||
static void _startIsolate(Function topLevel,
|
||||
List<String> args, message,
|
||||
bool isSpawnUri,
|
||||
bool startPaused,
|
||||
SendPort replyTo) {
|
||||
_IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT();
|
||||
Primitives.initializeStatics(context.id);
|
||||
|
@ -763,14 +782,25 @@ class IsolateNatives {
|
|||
replyTo.send([_SPAWNED_SIGNAL,
|
||||
context.controlPort.sendPort,
|
||||
context.pauseCapability]);
|
||||
if (!isSpawnUri) {
|
||||
topLevel(message);
|
||||
} else if (topLevel is _MainFunctionArgsMessage) {
|
||||
topLevel(args, message);
|
||||
} else if (topLevel is _MainFunctionArgs) {
|
||||
topLevel(args);
|
||||
|
||||
void runStartFunction() {
|
||||
if (!isSpawnUri) {
|
||||
topLevel(message);
|
||||
} else if (topLevel is _MainFunctionArgsMessage) {
|
||||
topLevel(args, message);
|
||||
} else if (topLevel is _MainFunctionArgs) {
|
||||
topLevel(args);
|
||||
} else {
|
||||
topLevel();
|
||||
}
|
||||
}
|
||||
|
||||
if (startPaused) {
|
||||
context.addPause(context.pauseCapability, context.pauseCapability);
|
||||
_globalState.topEventLoop.enqueue(context, runStartFunction,
|
||||
'start isolate');
|
||||
} else {
|
||||
topLevel();
|
||||
runStartFunction();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,6 +811,7 @@ class IsolateNatives {
|
|||
static void _spawnWorker(functionName, String uri,
|
||||
List<String> args, message,
|
||||
bool isSpawnUri,
|
||||
bool startPaused,
|
||||
SendPort replyPort) {
|
||||
if (uri == null) uri = thisScript;
|
||||
final worker = JS('var', 'new Worker(#)', uri);
|
||||
|
@ -805,6 +836,7 @@ class IsolateNatives {
|
|||
'args': args,
|
||||
'msg': _serializeMessage(message),
|
||||
'isSpawnUri': isSpawnUri,
|
||||
'startPaused': startPaused,
|
||||
'functionName': functionName }));
|
||||
}
|
||||
}
|
||||
|
@ -844,7 +876,6 @@ class _NativeJsSendPort extends _BaseSendPort implements SendPort {
|
|||
final isolate = _globalState.isolates[_isolateId];
|
||||
if (isolate == null) return;
|
||||
if (_receivePort._isClosed) return;
|
||||
|
||||
// We force serialization/deserialization as a simple way to ensure
|
||||
// isolate communication restrictions are respected between isolates that
|
||||
// live in the same worker. [_NativeJsSendPort] delivers both messages
|
||||
|
|
|
@ -12,9 +12,10 @@ import 'dart:_isolate_helper' show CapabilityImpl,
|
|||
RawReceivePortImpl;
|
||||
|
||||
patch class Isolate {
|
||||
patch static Future<Isolate> spawn(void entryPoint(message), var message) {
|
||||
patch static Future<Isolate> spawn(void entryPoint(message), var message,
|
||||
{ bool paused: false }) {
|
||||
try {
|
||||
return IsolateNatives.spawnFunction(entryPoint, message)
|
||||
return IsolateNatives.spawnFunction(entryPoint, message, paused)
|
||||
.then((msg) => new Isolate._fromControlPort(msg[1], msg[2]));
|
||||
} catch (e, st) {
|
||||
return new Future<Isolate>.error(e, st);
|
||||
|
@ -22,7 +23,7 @@ patch class Isolate {
|
|||
}
|
||||
|
||||
patch static Future<Isolate> spawnUri(
|
||||
Uri uri, List<String> args, var message) {
|
||||
Uri uri, List<String> args, var message, { bool paused: false }) {
|
||||
try {
|
||||
if (args is List<String>) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
|
@ -33,7 +34,7 @@ patch class Isolate {
|
|||
} else if (args != null) {
|
||||
throw new ArgumentError("Args must be a list of Strings $args");
|
||||
}
|
||||
return IsolateNatives.spawnUri(uri, args, message)
|
||||
return IsolateNatives.spawnUri(uri, args, message, paused)
|
||||
.then((msg) => new Isolate._fromControlPort(msg[1], msg[2]));
|
||||
} catch (e, st) {
|
||||
return new Future<Isolate>.error(e, st);
|
||||
|
|
|
@ -58,7 +58,8 @@ class Isolate {
|
|||
* Returns a future that will complete with an [Isolate] instance if the
|
||||
* spawning succeeded. It will complete with an error otherwise.
|
||||
*/
|
||||
external static Future<Isolate> spawn(void entryPoint(message), var message);
|
||||
external static Future<Isolate> spawn(void entryPoint(message), var message,
|
||||
{ bool paused: false });
|
||||
|
||||
/**
|
||||
* Creates and spawns an isolate that runs the code from the library with
|
||||
|
@ -80,7 +81,7 @@ class Isolate {
|
|||
* spawning succeeded. It will complete with an error otherwise.
|
||||
*/
|
||||
external static Future<Isolate> spawnUri(
|
||||
Uri uri, List<String> args, var message);
|
||||
Uri uri, List<String> args, var message, { bool paused: false });
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,8 +10,9 @@ isolate_stress_test: Fail # Issue 12588: This should be able to pass when we hav
|
|||
serialization_test: SkipByDesign # Tests dart2js-specific serialization code
|
||||
isolate_throws_test/01: Skip # Issue 12587
|
||||
compile_time_error_test/01: Skip # Issue 12587
|
||||
capability_test: Fail # Not implemented yet
|
||||
pause_test: Fail # Not implemented yet
|
||||
capability_test: Fail # Not implemented yet
|
||||
pause_test: Fail # Not implemented yet
|
||||
start_paused_test: Fail # Not implemented yet
|
||||
|
||||
[ $compiler == dart2js && $jscl ]
|
||||
browser/*: SkipByDesign # Browser specific tests
|
||||
|
|
81
tests/isolate/start_paused_test.dart
Normal file
81
tests/isolate/start_paused_test.dart
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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 start_paused_test;
|
||||
|
||||
import "dart:isolate";
|
||||
import "package:expect/expect.dart";
|
||||
import "package:async_helper/async_helper.dart";
|
||||
|
||||
void isomain(SendPort p) {
|
||||
p.send("DONE");
|
||||
}
|
||||
|
||||
void notyet(_) {
|
||||
throw "NOT YET";
|
||||
}
|
||||
|
||||
void main() {
|
||||
asyncStart();
|
||||
test1();
|
||||
test2();
|
||||
asyncEnd();
|
||||
}
|
||||
|
||||
void test1() {
|
||||
// Test that a paused isolate doesn't send events.
|
||||
// We start two isolates, one paused and one not.
|
||||
// The unpaused one must send an event, after which
|
||||
// we resume that paused isolate, and expect the second event.
|
||||
// This is not a guaranteed test, since it can succeede even if the
|
||||
// paused isolate isn't really paused.
|
||||
// However, it must never fail, since that would mean that a paused
|
||||
// isolate sends a message.
|
||||
asyncStart();
|
||||
RawReceivePort p1 = new RawReceivePort(notyet);
|
||||
Isolate.spawn(isomain, p1.sendPort, paused: true)
|
||||
.then((isolate) {
|
||||
RawReceivePort p2;
|
||||
p2 = new RawReceivePort((x) {
|
||||
Expect.equals("DONE", x);
|
||||
p2.close();
|
||||
p1.handler = (x) {
|
||||
Expect.equals("DONE", x);
|
||||
p1.close();
|
||||
asyncEnd();
|
||||
};
|
||||
isolate.resume(isolate.pauseCapability);
|
||||
});
|
||||
Isolate.spawn(isomain, p2.sendPort);
|
||||
});
|
||||
}
|
||||
|
||||
void test2() {
|
||||
// Test that a paused isolate doesn't send events.
|
||||
// Like the test above, except that we change the pause capability
|
||||
// of the paused isolate by pausing it using another capability and
|
||||
// then resuming the initial pause. This must not cause it to send
|
||||
// a message before the second pause is resumed as well.
|
||||
asyncStart();
|
||||
RawReceivePort p1 = new RawReceivePort(notyet);
|
||||
Isolate.spawn(isomain, p1.sendPort, paused: true)
|
||||
.then((isolate) {
|
||||
RawReceivePort p2;
|
||||
Capability c2 = new Capability();
|
||||
// Switch to another pause capability.
|
||||
isolate.pause(c2);
|
||||
isolate.resume(isolate.pauseCapability);
|
||||
p2 = new RawReceivePort((x) {
|
||||
Expect.equals("DONE", x);
|
||||
p2.close();
|
||||
p1.handler = (x) {
|
||||
Expect.equals("DONE", x);
|
||||
p1.close();
|
||||
asyncEnd();
|
||||
};
|
||||
isolate.resume(c2);
|
||||
});
|
||||
Isolate.spawn(isomain, p2.sendPort);
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue