Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: frog/lib/isolate.dart

Issue 8577003: Convert most of the javascript isolate code into Dart, inject JS code only when (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /**
6 * A native object that is shared across isolates. This object is visible to all
7 * isolates running on the same worker (either UI or background web worker).
8 *
9 * This is code that is intended to 'escape' the isolate boundaries in order to
10 * implement the semantics of friendly isolates in JavaScript. Without this we
11 * would have been forced to implement more code (including the top-level event
12 * loop) in JavaScript itself.
13 */
14 GlobalState get _globalState() native "return \$globalState;";
15 set _globalState(GlobalState val) native "\$globalState = val;";
16
17 /**
18 * Wrapper that takes the dart entry point and runs it within an isolate. The
19 * frog compiler will inject a call of the form [: startAsIsolate(main); :] when
20 * it determines that this wrapping is needed. For single-isolate applications
21 * (e.g. hello world), this call is not emited.
mattsh 2011/11/16 19:09:17 emitted
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done.
22 */
23 void startAsIsolate(entry) {
mattsh 2011/11/16 19:09:17 startRootIsolate
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done.
24 _globalState = new GlobalState();
25
26 // Don't start the main loop again, if we are in a worker.
27 if (_globalState.inWorker) return;
28 final entryIsolate = new IsolateContext();
mattsh 2011/11/16 19:09:17 maybe rootContext
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done.
29 _globalState.rootIsolate = entryIsolate;
30
31 // BUG(5151491): Setting _thisISolate should not be necessary, but because
32 // closures passed to the DOM as event handlers do not bind their isolate
33 // automatically we try to give them a reasonable context to live in by having
34 // a "default" isolate (the first one created).
35 _globalState.currentIsolate = entryIsolate;
jimhug 2011/11/16 18:00:45 I'm glad there's a bug here - this should be fixed
36
37 entryIsolate.eval(entry);
38 _globalState.topEventLoop.run();
39 }
40
41 /** Global state associated with the current worker. See [_globalState]. */
mattsh 2011/11/16 19:09:17 mabye call this WorkerState or ThreadState? It lo
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 yeah - this is a nice suggestion. I'm hope that we
42 class GlobalState {
43
44 /** Next available isolate id. */
45 int nextIsolateId = 0;
46
47 /** Worker id associated with this worker. */
48 int currentWorkerId = 0;
49
50 /**
51 * Next available worker id. Only used by the main worker to assign a unique
52 * id to each worker created by it.
53 */
54 int nextWorkerId = 1;
55
56 /** Context for the currently running [Isolate]. */
57 IsolateContext currentIsolate = null;
mattsh 2011/11/16 19:09:17 isolateContext
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done
58
59 /** Context for the root [Isolate] that first run in this worker. */
60 IsolateContext rootIsolate = null;
mattsh 2011/11/16 19:09:17 rootContext
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done.
61
62 /** The top-level event loop. */
63 EventLoop topEventLoop;
64
65 /** Whether this program is running in a background worker. */
66 bool inWorker;
mattsh 2011/11/16 19:09:17 inWorker suggests this value might change. Perhap
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done.
67
68 /** Whether this program is running in a UI worker. */
69 bool inWindow;
70
71 /** Whether we support spawning workers. */
72 bool supportsWorkers;
73
74 /**
75 * Whether to use web workers when implementing isolates. Set to false for
76 * debugging/testing.
77 */
78 bool get useWorkers() => supportsWorkers;
Jennifer Messerly 2011/11/17 02:28:11 a lot of these properties are redundant? Should th
Siggi Cherem (dart-lang) 2011/11/17 16:50:11 For now the 'set to false' is only done by modifyi
79
80 /**
81 * Whether to use the web-worker JSON-based message serialization protocol,
82 * even if not using web workers. Set to true to always use the web-worker
83 * JSON-based message serialization protocol, e.g. for testing purposes.
84 */
85 bool get useWorkerSerializationProtocol() => useWorkers;
Jennifer Messerly 2011/11/17 02:28:11 Was this an old name? It's reeeeeeallllllyyyy long
Siggi Cherem (dart-lang) 2011/11/17 16:50:11 I know :) - copied from JS. I'll think of somethin
86
87 /**
88 * Registry of isolates. Isolates must be registered if, and only if, receive
89 * ports are alive. Normally no open receive-ports means that the isolate is
90 * dead, but DOM callbacks could resurrect it.
91 */
92 Map<int, IsolateContext> isolates;
93
94 /** Reference to the main worker. */
95 MainWorker mainWorker;
mattsh 2011/11/16 19:09:17 I think we're using somewhat confusing terminology
96
97 /** Registry of active workers. Only used in the main worker. */
98 Map<int, var> workers;
mattsh 2011/11/16 19:09:17 I think let's have a separate structure for fields
99
100 GlobalState() {
101 topEventLoop = new EventLoop();
102 isolates = {};
103 workers = {};
104 mainWorker = new MainWorker();
105 _nativeInit();
106 }
107
108 void _nativeInit() native @"""
109 this.inWorker = typeof ($globalThis['importScripts']) != 'undefined';
110 this.inWindow = typeof(window) !== 'undefined';
111 this.supportsWorkers = this.inWorker ||
112 ((typeof $globalThis['Worker']) != 'undefined');
113
114 // if workers are supported, treat this as a main worker:
115 if (this.supportsWorkers) {
116 $globalThis.onmessage = function(e) {
117 IsolateNatives._processWorkerMessage(this.mainWorker, e);
Jennifer Messerly 2011/11/17 02:28:11 does the compiler know to emit this if _nativeInit
Siggi Cherem (dart-lang) 2011/11/17 16:50:11 It doesn't - fortunately that method is also reach
118 };
119 }
120 """;
121
122 /**
123 * Close the worker running this code, called when there is nothing else to
124 * run.
125 */
126 void closeWorker() {
127 if (inWorker) {
128 if (!isolates.isEmpty()) return;
129 mainWorker.postMessage(
130 _serializeMessage({'command': 'close'}));
131 } else if (isolates.containsKey(rootIsolate.id) && workers.isEmpty() &&
132 !supportsWorkers && !inWindow) {
133 // This should only trigger when running on the command-line.
134 // We don't want this check to execute in the browser where the isolate
135 // might still be alive due to DOM callbacks.
136 throw new Exception("Program exited with open ReceivePorts.");
137 }
138 }
139 }
140
141 _serializeMessage(message) {
142 if (_globalState.useWorkerSerializationProtocol) {
143 return new Serializer().traverse(message);
144 } else {
145 return new Copier().traverse(message);
146 }
147 }
148
149 _deserializeMessage(message) {
150 if (_globalState.useWorkerSerializationProtocol) {
151 return new Deserializer().deserialize(message);
152 } else {
153 // Nothing more to do.
154 return message;
155 }
156 }
157
158 /** Default worker. */
159 class MainWorker {
160 int id = 0;
161 void postMessage(msg) native "return \$globalThis.postMessage(msg);";
162 }
163
164 /** Context information tracked for each isolate. */
165 class IsolateContext {
166 /** Current isolate id. */
167 int id;
168
169 /** Registry of receive ports currently active on this isolate. */
170 Map<int, ReceivePort> ports;
171
172 /** Holds isolate globals (statics and top-level properties). */
173 var isolateStatics; // native object containing all globals of an isolate.
174
175 IsolateContext() {
176 id = _globalState.nextIsolateId++;
177 ports = {};
178 initGlobals();
179 }
180
181 // TODO(sigmund): actually do the initialization too.
182 void initGlobals() native "this.isolateStatics = {};";
183
184 /**
185 * Run [code] in the context of the isolate represented by [this]. Note this
186 * is marked as native because it is called from JavaScript (see $wrap_call in
187 * corejs.dart).
188 */
189 void eval(Function code) native {
190 var old = _globalState.currentIsolate;
191 _globalState.currentIsolate = this;
192 var result = null;
193 try {
194 result = code();
195 } finally {
196 _globalState.currentIsolate = old;
197 }
198 return result;
199 }
200
201 /** Lookup a port registered for this isolate. */
202 ReceivePort lookup(int id) => ports[id];
203
204 /** Register a port on this isolate. */
205 void register(int portId, ReceivePort port) {
206 if (ports.containsKey(portId)) {
207 throw new Exception("Registry: ports must be registered only once.");
208 }
209 ports[portId] = port;
210 _globalState.isolates[id] = this; // indicate this isolate is active
211 }
212
213 /** Unregister a port on this isolate. */
214 void unregister(int portId) {
215 ports.remove(portId);
216 if (ports.isEmpty()) {
217 _globalState.isolates.remove(id); // indicate this isolate is not active
218 }
219 }
220 }
221
222 /** Represent the event loop on a javascript thread (DOM or worker). */
223 class EventLoop {
224 Queue<IsolateEvent> events;
225
226 EventLoop() : events = new Queue<IsolateEvent>();
227
228 void enqueue(isolate, fn, msg) {
229 events.addLast(new IsolateEvent(isolate, fn, msg));
230 }
231
232 IsolateEvent dequeue() {
233 if (events.isEmpty()) return null;
234 return events.removeFirst();
235 }
236
237 /** Process a single event, if any. */
238 bool runIteration() {
239 final event = dequeue();
240 if (event == null) {
241 _globalState.closeWorker();
242 return false;
243 }
244 event.process();
245 return true;
246 }
247
248 /** Function equivalent to [:window.setTimeout:] when available, or null. */
249 static Function _platformDefer() native """
mattsh 2011/11/16 19:09:17 platformDefer is kind of vague, suggest rename to
Siggi Cherem (dart-lang) 2011/11/17 02:03:04 Done.
250 return typeof window != 'undefined' ?
251 function(a, b) { window.setTimeout(a, b); } : undefined;
252 """;
253
254 /**
255 * Runs multiple iterations of the run-loop. If possible, each iteration is
256 * run asynchronously.
257 */
258 void _runHelper() {
259 final setTimeout = _platformDefer();
260 if (setTimeout != null) {
261 // Run each iteration from the browser's top event loop.
262 void next() {
263 if (!runIteration()) return;
264 setTimeout(next, 0);
265 }
266 next();
267 } else {
268 // Run synchronously until no more iterations are available.
269 while (runIteration()) {}
270 }
271 }
272
273 /**
274 * Call [_runHelper] but ensure that worker exceptions are propragated. Note
275 * this is marked as native because it is called from JavaScript (see
276 * $wrap_call in corejs.dart).
277 */
278 void run() native {
279 if (!_globalState.inWorker) {
280 _runHelper();
281 } else {
282 try {
283 _runHelper();
284 } catch(e) {
285 // TODO(floitsch): try to send stack-trace to the other side.
286 _globalState.mainWorker.postMessage(_serializeMessage(
287 {'command': 'error', 'msg': "" + e }));
288 }
289 }
290 }
291 }
292
293 /** An event in the top-level event queue. */
294 class IsolateEvent {
295 IsolateContext isolate;
296 Function fn;
297 String message;
298
299 IsolateEvent(this.isolate, this.fn, this.message);
300
301 void process() {
302 isolate.eval(fn);
303 }
304 }
305
306 /** Implementation of a send port on top of JavaScript. */
5 class SendPortImpl implements SendPort { 307 class SendPortImpl implements SendPort {
6 308
7 const SendPortImpl(this._workerId, this._isolateId, this._receivePortId); 309 const SendPortImpl(this._workerId, this._isolateId, this._receivePortId);
8 310
9 void send(var message, [SendPort replyTo = null]) { 311 void send(var message, [SendPort replyTo = null]) {
10 if (replyTo !== null && !(replyTo is SendPortImpl)) { 312 if (replyTo !== null && !(replyTo is SendPortImpl)) {
11 throw "SendPort::send: Illegal replyTo type."; 313 throw "SendPort::send: Illegal replyTo type.";
12 } 314 }
13 IsolateNatives.sendMessage(_workerId, _isolateId, _receivePortId, 315 IsolateNatives._sendMessage(_workerId, _isolateId, _receivePortId,
14 _serializeMessage(message), _serializeMessage(replyTo)); 316 _serializeMessage(message), _serializeMessage(replyTo));
15 } 317 }
16 318
17 // TODO(sigmund): get rid of _sendNow 319 // TODO(sigmund): get rid of _sendNow (still used in corelib code)
18 void _sendNow(var message, replyTo) { send(message, replyTo); } 320 void _sendNow(var message, replyTo) { send(message, replyTo); }
19 321
20 _serializeMessage(message) {
21 if (IsolateNatives.shouldSerialize) {
22 return _IsolateJsUtil._serializeObject(message);
23 } else {
24 return _IsolateJsUtil._copyObject(message);
25 }
26 }
27
28 ReceivePortSingleShotImpl call(var message) { 322 ReceivePortSingleShotImpl call(var message) {
29 final result = new ReceivePortSingleShotImpl(); 323 final result = new ReceivePortSingleShotImpl();
30 this.send(message, result.toSendPort()); 324 this.send(message, result.toSendPort());
31 return result; 325 return result;
32 } 326 }
33 327
34 ReceivePortSingleShotImpl _callNow(var message) { 328 ReceivePortSingleShotImpl _callNow(var message) {
35 final result = new ReceivePortSingleShotImpl(); 329 final result = new ReceivePortSingleShotImpl();
36 send(message, result.toSendPort()); 330 send(message, result.toSendPort());
37 return result; 331 return result;
38 } 332 }
39 333
40 bool operator==(var other) { 334 bool operator==(var other) {
41 return (other is SendPortImpl) && 335 return (other is SendPortImpl) &&
42 (_workerId == other._workerId) && 336 (_workerId == other._workerId) &&
43 (_isolateId == other._isolateId) && 337 (_isolateId == other._isolateId) &&
44 (_receivePortId == other._receivePortId); 338 (_receivePortId == other._receivePortId);
45 } 339 }
46 340
47 int hashCode() { 341 int hashCode() {
48 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; 342 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId;
49 } 343 }
50 344
51 final int _receivePortId; 345 final int _receivePortId;
52 final int _isolateId; 346 final int _isolateId;
53 final int _workerId; 347 final int _workerId;
54
55 static _create(int workerId, int isolateId, int receivePortId) native {
56 return new SendPortImpl(workerId, isolateId, receivePortId);
57 }
58 static _getReceivePortId(SendPortImpl port) native {
59 return port._receivePortId;
60 }
61 static _getIsolateId(SendPortImpl port) native {
62 return port._isolateId;
63 }
64 static _getWorkerId(SendPortImpl port) native {
65 return port._workerId;
66 }
67 } 348 }
68 349
69 350 /** Default factory for receive ports. */
70 class ReceivePortFactory { 351 class ReceivePortFactory {
71 352
72 factory ReceivePort() { 353 factory ReceivePort() {
73 return new ReceivePortImpl(); 354 return new ReceivePortImpl();
74 } 355 }
75 356
76 factory ReceivePort.singleShot() { 357 factory ReceivePort.singleShot() {
77 return new ReceivePortSingleShotImpl(); 358 return new ReceivePortSingleShotImpl();
78 } 359 }
79
80 } 360 }
81 361
82 362 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */
83 class ReceivePortImpl implements ReceivePort { 363 class ReceivePortImpl implements ReceivePort {
84 ReceivePortImpl() 364 ReceivePortImpl()
85 : _id = _nextFreeId++ { 365 : _id = _nextFreeId++ {
86 IsolateNatives.registerPort(_id, this); 366 _globalState.currentIsolate.register(_id, this);
87 } 367 }
88 368
89 void receive(void onMessage(var message, SendPort replyTo)) { 369 void receive(void onMessage(var message, SendPort replyTo)) {
90 _callback = onMessage; 370 _callback = onMessage;
91 } 371 }
92 372
93 void close() { 373 void close() {
94 _callback = null; 374 _callback = null;
95 IsolateNatives.unregisterPort(_id); 375 _globalState.currentIsolate.unregister(_id);
96 }
97
98 SendPort toSendPort() {
99 return _toNewSendPort();
100 } 376 }
101 377
102 /** 378 /**
103 * Returns a fresh [SendPort]. The implementation is not allowed to cache 379 * Returns a fresh [SendPort]. The implementation is not allowed to cache
104 * existing ports. 380 * existing ports.
105 */ 381 */
106 SendPort _toNewSendPort() { 382 SendPort toSendPort() {
107 return new SendPortImpl( 383 return new SendPortImpl(
108 IsolateNatives._currentWorkerId(), 384 _globalState.currentWorkerId, _globalState.currentIsolate.id, _id);
109 IsolateNatives._currentIsolateId(), _id);
110 } 385 }
111 386
112 int _id; 387 int _id;
113 Function _callback; 388 Function _callback;
114 389
115 static int _nextFreeId = 1; 390 static int _nextFreeId = 1;
116
117 static int _getId(ReceivePortImpl port) native {
118 return port._id;
119 }
120
121 static Function _getCallback(ReceivePortImpl port) native {
122 return port._callback;
123 }
124 } 391 }
125 392
126 393 /** Implementation of a single-shot [ReceivePort]. */
127 class ReceivePortSingleShotImpl implements ReceivePort { 394 class ReceivePortSingleShotImpl implements ReceivePort {
128 395
129 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } 396 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { }
130 397
131 void receive(void callback(var message, SendPort replyTo)) { 398 void receive(void callback(var message, SendPort replyTo)) {
132 _port.receive((var message, SendPort replyTo) { 399 _port.receive((var message, SendPort replyTo) {
133 _port.close(); 400 _port.close();
134 callback(message, replyTo); 401 callback(message, replyTo);
135 }); 402 });
136 } 403 }
137 404
138 void close() { 405 void close() {
139 _port.close(); 406 _port.close();
140 } 407 }
141 408
142 SendPort toSendPort() { 409 SendPort toSendPort() => _port.toSendPort();
143 return _toNewSendPort();
144 }
145
146 /**
147 * Returns a fresh [SendPort]. The implementation is not allowed to cache
148 * existing ports.
149 */
150 SendPort _toNewSendPort() {
151 return _port._toNewSendPort();
152 }
153 410
154 final ReceivePortImpl _port; 411 final ReceivePortImpl _port;
155
156 } 412 }
157 413
158 final String _SPAWNED_SIGNAL = "spawned"; 414 final String _SPAWNED_SIGNAL = "spawned";
159 415
160 class IsolateNatives native "IsolateNatives" { 416 class IsolateNatives {
417
418 /** JavaScript-specific implementation to spawn an isolate. */
161 static Future<SendPort> spawn(Isolate isolate, bool isLight) { 419 static Future<SendPort> spawn(Isolate isolate, bool isLight) {
162 Completer<SendPort> completer = new Completer<SendPort>(); 420 Completer<SendPort> completer = new Completer<SendPort>();
163 ReceivePort port = new ReceivePort.singleShot(); 421 ReceivePort port = new ReceivePort.singleShot();
164 port.receive((msg, SendPort replyPort) { 422 port.receive((msg, SendPort replyPort) {
165 assert(msg == _SPAWNED_SIGNAL); 423 assert(msg == _SPAWNED_SIGNAL);
166 completer.complete(replyPort); 424 completer.complete(replyPort);
167 }); 425 });
168 _spawn(isolate, isLight, port.toSendPort()); 426
169 if (false) { 427 // TODO(floitsch): throw exception if isolate's class doesn't have a
170 // TODO(sigmund): delete this code. This is temporarily added because we 428 // default constructor.
171 // are tree-shaking methods that are only reachable from js 429 if (_globalState.useWorkers && !isLight) {
172 _IsolateJsUtil._startIsolate(null, null); 430 _startWorker(isolate, port.toSendPort());
173 _IsolateJsUtil._deserializeMessage(null); 431 } else {
174 _IsolateJsUtil._print(null); 432 _startNonWorker(isolate, port.toSendPort());
175 } 433 }
434
176 return completer.future; 435 return completer.future;
177 } 436 }
178 437
179 static SendPort _spawn(Isolate isolate, bool light, SendPort port) native; 438 static SendPort _startWorker(Isolate runnable, SendPort replyPort) {
180 439 var factoryName = _getJSConstructorName(runnable);
181 static bool get shouldSerialize() native; 440 if (_globalState.inWorker) {
182 441 _globalState.mainWorker.postMessage(_serializeMessage({
183 static void sendMessage(int workerId, int isolateId, int receivePortId, 442 'command': 'spawn-worker',
184 message, replyTo) native; 443 'factoryName': factoryName,
185 444 'replyPort': replyPort}));
186 /** Registers an active receive port. */ 445 } else {
187 static void registerPort(int id, ReceivePort port) native; 446 _spawnWorker(factoryName, _serializeMessage(replyPort));
188 447 }
189 /** Unregister an inactive receive port. */ 448 }
190 static void unregisterPort(int id) native; 449
191 450
192 static int _currentWorkerId() native; 451 /**
193 452 * The src url for the script tag that loaded this code. Used to create
194 static int _currentIsolateId() native; 453 * JavaScript workers.
195 } 454 */
196 455 static String get _thisScript() =>
197 456 _thisScriptCache != null ? _thisScriptCache : _computeThisScript();
198 class _IsolateJsUtil native "_IsolateJsUtil" { 457
199 static void _startIsolate(Isolate isolate, SendPort replyTo) native { 458 static String _thisScriptCache;
459
460 // TODO(sigmund): fix - this code should be run synchronously when loading the
461 // script. Running lazily on DOMContentLoaded will yield incorrect results.
462 static String _computeThisScript() native @"""
463 if (!$globalState.supportsWorkers || $globalState.inWorker) return null;
464
465 // TODO(5334778): Find a cross-platform non-brittle way of getting the
466 // currently running script.
467 var scripts = document.getElementsByTagName('script');
468 // The scripts variable only contains the scripts that have already been
469 // executed. The last one is the currently running script.
470 var script = scripts[scripts.length - 1];
471 var src = script.src;
472 if (!src) {
473 // TODO()
474 src = "FIXME:5407062" + "_" + Math.random().toString();
475 script.src = src;
476 }
477 IsolateNatives._thisScriptCache = src;
478 return src;
479 """;
480
481 /** Starts a new worker with the given URL. */
482 static void _newWorker(url) native "return new Worker(url)";
483
484 /**
485 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor
486 * name for the isolate entry point class.
487 */
488 static void _spawnWorker(factoryName, serializedReplyPort) {
489 var worker = _newWorker(_thisScript);
490 // TODO(sigmund): make this work.
491 worker.onmessage = function(e) {
492 _processWorkerMessage(worker, e);
493 };
494 var workerId = _globalState.nextWorkerId++;
495 // We also store the id on the worker itself so that we can unregister it.
496 worker.id = workerId;
497 _globalState.workers[workerId] = worker;
498 worker.postMessage(_serializeMessage({
499 'command': 'start',
500 'id': workerId,
501 'replyTo': serializedReplyPort,
502 'factoryName': factoryName }));
503 }
504
505 /**
506 * Process messages on a worker, either to control the worker instance or to
507 * pass messages along to the isolate running in the worker.
508 */
509 static void _processWorkerMessage(sender, e) {
510 var msg = _deserializeMessage(e.data);
511 switch (msg['command']) {
512 case 'start':
513 _log("starting worker: " + msg['id'] + " " + msg['factoryName']);
514 _globalState.currentWorkerId = msg['id'];
515 var runnerObject =
516 _allocate(_getJSConstructorFromName(msg['factoryName']));
517 var serializedReplyTo = msg['replyTo'];
518 _globalState.topEventLoop.enqueue(new IsolateContext(), function() {
519 var replyTo = _deserializeMessage(serializedReplyTo);
520 IsolateNatives._startIsolate(runnerObject, replyTo);
521 }, 'worker-start');
522 _globalState.topEventLoop.run();
523 break;
524 case 'spawn-worker':
525 _spawnWorker(msg['factoryName'], msg['replyPort']);
526 break;
527 case 'message':
528 _sendMessage(msg['workerId'], msg['isolateId'], msg['portId'],
529 msg['msg'], msg['replyTo']);
530 _globalState.topEventLoop.run();
531 break;
532 case 'close':
533 _log("Closing Worker");
534 _globalState.workers.remove(sender.id);
535 sender.terminate();
536 _globalState.topEventLoop.run();
537 break;
538 case 'log':
539 _log(msg['msg']);
540 break;
541 case 'print':
542 if (_globalState.inWorker) {
543 _globalState.mainWorker.postMessage(
544 _serializeMessage({'command': 'print', 'msg': msg}));
545 } else {
546 print(msg['msg']);
547 }
548 break;
549 case 'error':
550 throw msg['msg'];
551 break;
552 }
553 }
554
555 /** Log a message, forwarding to the main worker if appropriate. */
556 static _log(msg) {
557 if (_globalState.inWorker) {
558 _globalState.mainWorker.postMessage({'command': 'log', 'msg': msg });
559 } else {
560 try {
561 _consoleLog(msg);
562 } catch(e, trace) {
563 throw new Exception(trace);
564 }
565 }
566 }
567
568 static void _consoleLog(msg) native "\$globalThis.console.log(msg);";
569
570
571 /**
572 * Extract the constructor of runnable, so it can be allocated in another
573 * isolate.
574 */
575 static var _getJSConstructor(Isolate runnable) native """
576 return runnable.constructor;
577 """;
578
579 /** Extract the constructor name of a runnable */
580 // TODO(sigmund): find a browser-generic way to support this.
581 static var _getJSConstructorName(Isolate runnable) native """
582 return runnable.constructor.name;
583 """;
584
585 /** Find a constructor given it's name. */
586 static var _getJSConstructorFromName(String factoryName) native """
587 return \$globalThis[factoryName];
588 """;
589
590 /** Create a new JavasSript object instance given it's constructor. */
591 static var _allocate(var ctor) native "return new ctor();";
592
593 /** Starts a non-worker isolate. */
594 static SendPort _startNonWorker(Isolate runnable, SendPort replyTo) {
595 // Spawn a new isolate and create the receive port in it.
596 final spawned = new IsolateContext();
597
598 // Instead of just running the provided runnable, we create a
599 // new cloned instance of it with a fresh state in the spawned
600 // isolate. This way, we do not get cross-isolate references
601 // through the runnable.
602 final ctor = _getJSConstructor(runnable);
603 _globalState.topEventLoop.enqueue(spawned, function() {
604 _startIsolate(_allocate(ctor), replyTo);
605 }, 'nonworker start');
606 }
607
608 /** Given a ready-to-start runnable, start running it. */
609 static void _startIsolate(Isolate isolate, SendPort replyTo) {
200 ReceivePort port = new ReceivePort(); 610 ReceivePort port = new ReceivePort();
201 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); 611 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
202 isolate._run(port); 612 isolate._run(port);
203 } 613 }
204 614
205 static void _print(String msg) native { 615 static void _sendMessage(int workerId, int isolateId, int receivePortId,
206 print(msg); 616 message, replyTo) {
207 } 617 // Both the message and the replyTo are already serialized.
208 618 if (workerId == _globalState.currentWorkerId) {
209 static _copyObject(obj) native { 619 var isolate = _globalState.isolates[isolateId];
210 return new Copier().traverse(obj); 620 if (isolate == null) return; // Isolate has been closed.
211 } 621 var receivePort = isolate.lookup(receivePortId);
212 622 if (receivePort == null) return; // ReceivePort has been closed.
213 static _serializeObject(obj) native { 623 _globalState.topEventLoop.enqueue(isolate, () {
214 return new Serializer().traverse(obj); 624 if (receivePort._callback != null) {
215 } 625 receivePort._callback(
216 626 _deserializeMessage(message), _deserializeMessage(replyTo));
217 static _deserializeMessage(message) native { 627 }
218 return new Deserializer().deserialize(message); 628 }, 'receive ' + message);
629 } else {
630 var worker;
631 // communication between workers go through the main worker
632 if (_globalState.inWorker) {
633 worker = _globalState.mainWorker;
634 } else {
635 // TODO(sigmund): make sure this works
636 worker = _globalState.workers[workerId];
637 }
638 worker.postMessage(_serializeMessage({
639 'command': 'message',
640 'workerId': workerId,
641 'isolateId': isolateId,
642 'portId': receivePortId,
643 'msg': message,
644 'replyTo': replyTo }));
645 }
219 } 646 }
220 } 647 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698