isolate in frog: hiding internal implementation classes, couple minor fixes.

BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com//9562048

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@4900 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
sigmund@google.com
2012-03-02 21:01:23 +00:00
parent 4bb75b2828
commit 3800841285
4 changed files with 87 additions and 90 deletions
+7 -7
View File
@@ -12,20 +12,20 @@
* applications (e.g. hello world), this call is not emitted.
*/
void startRootIsolate(entry) {
globalState = new GlobalState();
_globalState = new _GlobalState();
// Don't start the main loop again, if we are in a worker.
if (globalState.isWorker) return;
final rootContext = new IsolateContext();
globalState.rootContext = rootContext;
fillStatics(rootContext);
if (_globalState.isWorker) return;
final rootContext = new _IsolateContext();
_globalState.rootContext = rootContext;
_fillStatics(rootContext);
// BUG(5151491): Setting currentContext should not be necessary, but
// because closures passed to the DOM as event handlers do not bind their
// isolate automatically we try to give them a reasonable context to live in
// by having a "default" isolate (the first one created).
globalState.currentContext = rootContext;
_globalState.currentContext = rootContext;
rootContext.eval(entry);
globalState.topEventLoop.run();
_globalState.topEventLoop.run();
}
+64 -67
View File
@@ -11,17 +11,17 @@
* been forced to implement more code (including the top-level event loop) in
* JavaScript itself.
*/
GlobalState get globalState() native "return \$globalState;";
set globalState(GlobalState val) native "\$globalState = val;";
_GlobalState get _globalState() native "return \$globalState;";
set _globalState(_GlobalState val) native "\$globalState = val;";
void fillStatics(context) native @"""
void _fillStatics(context) native @"""
$globals = context.isolateStatics;
$static_init();
""";
/** Global state associated with the current worker. See [globalState]. */
// TODO(sigmund): split in multiple classes: global, thread, main-worker states?
class GlobalState {
class _GlobalState {
/** Next available isolate id. */
int nextIsolateId = 0;
@@ -36,13 +36,13 @@ class GlobalState {
int nextWorkerId = 1;
/** Context for the currently running [Isolate]. */
IsolateContext currentContext = null;
_IsolateContext currentContext = null;
/** Context for the root [Isolate] that first run in this worker. */
IsolateContext rootContext = null;
_IsolateContext rootContext = null;
/** The top-level event loop. */
EventLoop topEventLoop;
_EventLoop topEventLoop;
/** Whether this program is running in a background worker. */
bool isWorker;
@@ -71,19 +71,19 @@ class GlobalState {
* ports are alive. Normally no open receive-ports means that the isolate is
* dead, but DOM callbacks could resurrect it.
*/
Map<int, IsolateContext> isolates;
Map<int, _IsolateContext> isolates;
/** Reference to the main worker. */
MainWorker mainWorker;
_MainWorker mainWorker;
/** Registry of active workers. Only used in the main worker. */
Map<int, Dynamic> workers;
GlobalState() {
topEventLoop = new EventLoop();
_GlobalState() {
topEventLoop = new _EventLoop();
isolates = {};
workers = {};
mainWorker = new MainWorker();
mainWorker = new _MainWorker();
_nativeInit();
}
@@ -92,10 +92,8 @@ class GlobalState {
this.inWindow = typeof(window) !== 'undefined';
this.supportsWorkers = this.isWorker ||
((typeof $globalThis['Worker']) != 'undefined');
// if workers are supported, treat this as a main worker:
if (this.supportsWorkers) {
$globalThis.onmessage = function(e) {
if (this.isWorker) {
$globalThis.onmessage = function (e) {
_IsolateNatives._processWorkerMessage(this.mainWorker, e);
};
}
@@ -124,7 +122,7 @@ class GlobalState {
}
/** Context information tracked for each isolate. */
class IsolateContext {
class _IsolateContext {
/** Current isolate id. */
int id;
@@ -134,8 +132,8 @@ class IsolateContext {
/** Holds isolate globals (statics and top-level properties). */
var isolateStatics; // native object containing all globals of an isolate.
IsolateContext() {
id = globalState.nextIsolateId++;
_IsolateContext() {
id = _globalState.nextIsolateId++;
ports = {};
initGlobals();
}
@@ -148,14 +146,14 @@ class IsolateContext {
* is called from JavaScript (see $wrap_call in corejs.dart).
*/
void eval(Function code) {
var old = globalState.currentContext;
globalState.currentContext = this;
var old = _globalState.currentContext;
_globalState.currentContext = this;
this._setGlobals();
var result = null;
try {
result = code();
} finally {
globalState.currentContext = old;
_globalState.currentContext = old;
if (old != null) old._setGlobals();
}
return result;
@@ -172,30 +170,30 @@ class IsolateContext {
throw new Exception("Registry: ports must be registered only once.");
}
ports[portId] = port;
globalState.isolates[id] = this; // indicate this isolate is active
_globalState.isolates[id] = this; // indicate this isolate is active
}
/** Unregister a port on this isolate. */
void unregister(int portId) {
ports.remove(portId);
if (ports.isEmpty()) {
globalState.isolates.remove(id); // indicate this isolate is not active
_globalState.isolates.remove(id); // indicate this isolate is not active
}
}
}
/** Represent the event loop on a javascript thread (DOM or worker). */
class EventLoop {
Queue<IsolateEvent> events;
class _EventLoop {
Queue<_IsolateEvent> events;
EventLoop() : events = new Queue<IsolateEvent>();
_EventLoop() : events = new Queue<_IsolateEvent>();
void enqueue(isolate, fn, msg) {
events.addLast(new IsolateEvent(isolate, fn, msg));
events.addLast(new _IsolateEvent(isolate, fn, msg));
}
IsolateEvent dequeue() {
_IsolateEvent dequeue() {
if (events.isEmpty()) return null;
return events.removeFirst();
}
@@ -204,7 +202,7 @@ class EventLoop {
bool runIteration() {
final event = dequeue();
if (event == null) {
globalState.closeWorker();
_globalState.closeWorker();
return false;
}
event.process();
@@ -241,13 +239,13 @@ class EventLoop {
* this is called from JavaScript (see $wrap_call in corejs.dart).
*/
void run() {
if (!globalState.isWorker) {
if (!_globalState.isWorker) {
_runHelper();
} else {
try {
_runHelper();
} catch(var e, var trace) {
globalState.mainWorker.postMessage(_serializeMessage(
_globalState.mainWorker.postMessage(_serializeMessage(
{'command': 'error', 'msg': '$e\n$trace' }));
}
}
@@ -255,12 +253,12 @@ class EventLoop {
}
/** An event in the top-level event queue. */
class IsolateEvent {
IsolateContext isolate;
class _IsolateEvent {
_IsolateContext isolate;
Function fn;
String message;
IsolateEvent(this.isolate, this.fn, this.message);
_IsolateEvent(this.isolate, this.fn, this.message);
void process() {
isolate.eval(fn);
@@ -269,10 +267,9 @@ class IsolateEvent {
/** Default worker. */
class MainWorker {
class _MainWorker {
int id = 0;
void postMessage(msg) native "return \$globalThis.postMessage(msg);";
void set onmessage(f) native "\$globalThis.onmessage = f;";
void postMessage(msg) native @"$globalThis.postMessage(msg);";
void terminate() {}
}
@@ -304,7 +301,7 @@ class _IsolateNatives {
// TODO(floitsch): throw exception if isolate's class doesn't have a
// default constructor.
if (globalState.useWorkers && !isLight) {
if (_globalState.useWorkers && !isLight) {
_startWorker(isolate, port.toSendPort());
} else {
_startNonWorker(isolate, port.toSendPort());
@@ -315,8 +312,8 @@ class _IsolateNatives {
static SendPort _startWorker(Isolate runnable, SendPort replyPort) {
var factoryName = _getJSConstructorName(runnable);
if (globalState.isWorker) {
globalState.mainWorker.postMessage(_serializeMessage({
if (_globalState.isWorker) {
_globalState.mainWorker.postMessage(_serializeMessage({
'command': 'spawn-worker',
'factoryName': factoryName,
'replyPort': _serializeMessage(replyPort)}));
@@ -368,10 +365,10 @@ class _IsolateNatives {
static void _spawnWorker(factoryName, serializedReplyPort) {
final worker = _newWorker(_thisScript);
worker.onmessage = (e) { _processWorkerMessage(worker, e); };
var workerId = globalState.nextWorkerId++;
var workerId = _globalState.nextWorkerId++;
// We also store the id on the worker itself so that we can unregister it.
worker.id = workerId;
globalState.workers[workerId] = worker;
_globalState.workers[workerId] = worker;
worker.postMessage(_serializeMessage({
'command': 'start',
'id': workerId,
@@ -395,24 +392,24 @@ class _IsolateNatives {
switch (msg['command']) {
// TODO(sigmund): delete after we migrate to the new API
case 'start':
globalState.currentWorkerId = msg['id'];
_globalState.currentWorkerId = msg['id'];
var runnerObject =
_allocate(_getJSConstructorFromName(msg['factoryName']));
var serializedReplyTo = msg['replyTo'];
globalState.topEventLoop.enqueue(new IsolateContext(), function() {
_globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
var replyTo = _deserializeMessage(serializedReplyTo);
_startIsolate(runnerObject, replyTo);
}, 'worker-start');
globalState.topEventLoop.run();
_globalState.topEventLoop.run();
break;
case 'start2':
globalState.currentWorkerId = msg['id'];
_globalState.currentWorkerId = msg['id'];
Function entryPoint = _getJSFunctionFromName(msg['functionName']);
var replyTo = _deserializeMessage(msg['replyTo']);
globalState.topEventLoop.enqueue(new IsolateContext(), function() {
_globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
_startIsolate2(entryPoint, replyTo);
}, 'worker-start');
globalState.topEventLoop.run();
_globalState.topEventLoop.run();
break;
// TODO(sigmund): delete after we migrate to the new API
case 'spawn-worker':
@@ -423,20 +420,20 @@ class _IsolateNatives {
break;
case 'message':
msg['port'].send(msg['msg'], msg['replyTo']);
globalState.topEventLoop.run();
_globalState.topEventLoop.run();
break;
case 'close':
_log("Closing Worker");
globalState.workers.remove(sender.id);
_globalState.workers.remove(sender.id);
sender.terminate();
globalState.topEventLoop.run();
_globalState.topEventLoop.run();
break;
case 'log':
_log(msg['msg']);
break;
case 'print':
if (globalState.isWorker) {
globalState.mainWorker.postMessage(
if (_globalState.isWorker) {
_globalState.mainWorker.postMessage(
_serializeMessage({'command': 'print', 'msg': msg}));
} else {
print(msg['msg']);
@@ -449,8 +446,8 @@ class _IsolateNatives {
/** Log a message, forwarding to the main worker if appropriate. */
static _log(msg) {
if (globalState.isWorker) {
globalState.mainWorker.postMessage(
if (_globalState.isWorker) {
_globalState.mainWorker.postMessage(
_serializeMessage({'command': 'log', 'msg': msg }));
} else {
try {
@@ -522,21 +519,21 @@ class _IsolateNatives {
/** Starts a non-worker isolate. */
static SendPort _startNonWorker(Isolate runnable, SendPort replyTo) {
// Spawn a new isolate and create the receive port in it.
final spawned = new IsolateContext();
final spawned = new _IsolateContext();
// Instead of just running the provided runnable, we create a
// new cloned instance of it with a fresh state in the spawned
// isolate. This way, we do not get cross-isolate references
// through the runnable.
final ctor = _getJSConstructor(runnable);
globalState.topEventLoop.enqueue(spawned, function() {
_globalState.topEventLoop.enqueue(spawned, function() {
_startIsolate(_allocate(ctor), replyTo);
}, 'nonworker start');
}
/** Given a ready-to-start runnable, start running it. */
static void _startIsolate(Isolate isolate, SendPort replyTo) {
fillStatics(globalState.currentContext);
_fillStatics(_globalState.currentContext);
ReceivePort port = new ReceivePort();
replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
isolate._run(port);
@@ -554,19 +551,19 @@ class _IsolateNatives {
SendPort signalReply = port.toSendPort();
if (globalState.useWorkers && !isLight) {
if (_globalState.useWorkers && !isLight) {
_startWorker2(functionName, uri, signalReply);
} else {
_startNonWorker2(functionName, uri, signalReply);
}
return new _BufferingSendPort(
globalState.currentContext.id, completer.future);
_globalState.currentContext.id, completer.future);
}
static SendPort _startWorker2(
String functionName, String uri, SendPort replyPort) {
if (globalState.isWorker) {
globalState.mainWorker.postMessage(_serializeMessage({
if (_globalState.isWorker) {
_globalState.mainWorker.postMessage(_serializeMessage({
'command': 'spawn-worker2',
'functionName': functionName,
'uri': uri,
@@ -581,14 +578,14 @@ class _IsolateNatives {
// TODO(eub): support IE9 using an iframe -- Dart issue 1702.
if (uri != null) throw new UnsupportedOperationException(
"Currently spawnUri is not supported without web workers.");
globalState.topEventLoop.enqueue(new IsolateContext(), function() {
_globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
final func = _getJSFunctionFromName(functionName);
_startIsolate2(func, replyPort);
}, 'nonworker start');
}
static void _startIsolate2(Function topLevel, SendPort replyTo) {
fillStatics(globalState.currentContext);
_fillStatics(_globalState.currentContext);
_port = new ReceivePort();
replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
topLevel();
@@ -609,10 +606,10 @@ class _IsolateNatives {
}
final worker = _newWorker(uri);
worker.onmessage = (e) { _processWorkerMessage(worker, e); };
var workerId = globalState.nextWorkerId++;
var workerId = _globalState.nextWorkerId++;
// We also store the id on the worker itself so that we can unregister it.
worker.id = workerId;
globalState.workers[workerId] = worker;
_globalState.workers[workerId] = worker;
worker.postMessage(_serializeMessage({
'command': 'start2',
'id': workerId,
+5 -5
View File
@@ -6,7 +6,7 @@
/** Serialize [message] (or simulate serialization). */
_serializeMessage(message) {
if (globalState.needSerialization) {
if (_globalState.needSerialization) {
return new _Serializer().traverse(message);
} else {
return new _Copier().traverse(message);
@@ -15,7 +15,7 @@ _serializeMessage(message) {
/** Deserialize [message] (or simulate deserialization). */
_deserializeMessage(message) {
if (globalState.needSerialization) {
if (_globalState.needSerialization) {
return new _Deserializer().deserialize(message);
} else {
// Nothing more to do.
@@ -200,7 +200,7 @@ class _Serializer extends _MessageTraverser {
}
visitNativeJsSendPort(_NativeJsSendPort port) {
return ['sendport', globalState.currentWorkerId,
return ['sendport', _globalState.currentWorkerId,
port._isolateId, port._receivePort._id];
}
@@ -307,8 +307,8 @@ class _Deserializer {
int receivePortId = x[3];
// If two isolates are in the same worker, we use NativeJsSendPorts to
// deliver messages directly without using postMessage.
if (workerId == globalState.currentWorkerId) {
var isolate = globalState.isolates[isolateId];
if (workerId == _globalState.currentWorkerId) {
var isolate = _globalState.isolates[isolateId];
if (isolate == null) return null; // Isolate has been closed.
var receivePort = isolate.lookup(receivePortId);
return new _NativeJsSendPort(receivePort, isolateId);
+11 -11
View File
@@ -54,7 +54,7 @@ class _NativeJsSendPort extends _BaseSendPort implements SendPort {
_waitForPendingPorts([message, replyTo], () {
checkReplyTo(replyTo);
// Check that the isolate still runs and the port is still open
final isolate = globalState.isolates[_isolateId];
final isolate = _globalState.isolates[_isolateId];
if (isolate == null) return;
if (_receivePort._callback == null) return;
@@ -64,16 +64,16 @@ class _NativeJsSendPort extends _BaseSendPort implements SendPort {
// from the same worker and messages from other workers. In particular,
// messages sent from a worker via a [_WorkerSendPort] are received at
// [_processWorkerMessage] and forwarded to a native port. In such cases,
// here we'll see [globalState.currentContext == null].
final shouldSerialize = globalState.currentContext != null
&& globalState.currentContext.id != _isolateId;
// here we'll see [_globalState.currentContext == null].
final shouldSerialize = _globalState.currentContext != null
&& _globalState.currentContext.id != _isolateId;
var msg = message;
var reply = replyTo;
if (shouldSerialize) {
msg = _serializeMessage(msg);
reply = _serializeMessage(reply);
}
globalState.topEventLoop.enqueue(isolate, () {
_globalState.topEventLoop.enqueue(isolate, () {
if (_receivePort._callback != null) {
if (shouldSerialize) {
msg = _deserializeMessage(msg);
@@ -108,11 +108,11 @@ class _WorkerSendPort extends _BaseSendPort implements SendPort {
'msg': message,
'replyTo': replyTo});
if (globalState.isWorker) {
if (_globalState.isWorker) {
// communication from one worker to another go through the main worker:
globalState.mainWorker.postMessage(workerMessage);
_globalState.mainWorker.postMessage(workerMessage);
} else {
globalState.workers[_workerId].postMessage(workerMessage);
_globalState.workers[_workerId].postMessage(workerMessage);
}
});
}
@@ -200,7 +200,7 @@ class _ReceivePortImpl implements ReceivePort {
_ReceivePortImpl()
: _id = _nextFreeId++ {
globalState.currentContext.register(_id, this);
_globalState.currentContext.register(_id, this);
}
void receive(void onMessage(var message, SendPort replyTo)) {
@@ -209,11 +209,11 @@ class _ReceivePortImpl implements ReceivePort {
void close() {
_callback = null;
globalState.currentContext.unregister(_id);
_globalState.currentContext.unregister(_id);
}
SendPort toSendPort() {
return new _NativeJsSendPort(this, globalState.currentContext.id);
return new _NativeJsSendPort(this, _globalState.currentContext.id);
}
}