OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 library _isolate_helper; | 5 library _isolate_helper; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection' show Queue, HashMap; | 8 import 'dart:collection' show Queue, HashMap; |
9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
10 import 'dart:_js_helper' show convertDartClosureToJS, | 10 import 'dart:_js_helper' show convertDartClosureToJS, |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
100 * "manager" - A manager contains one or more isolates, schedules their | 100 * "manager" - A manager contains one or more isolates, schedules their |
101 * execution, and performs other plumbing on their behalf. The isolate | 101 * execution, and performs other plumbing on their behalf. The isolate |
102 * present at the creation of the manager is designated as its "root isolate". | 102 * present at the creation of the manager is designated as its "root isolate". |
103 * A manager may, for example, be implemented on a web Worker. | 103 * A manager may, for example, be implemented on a web Worker. |
104 * | 104 * |
105 * [_Manager] - State present within a manager (exactly once, as a global). | 105 * [_Manager] - State present within a manager (exactly once, as a global). |
106 * | 106 * |
107 * [_ManagerStub] - A handle held within one manager that allows interaction | 107 * [_ManagerStub] - A handle held within one manager that allows interaction |
108 * with another manager. A target manager may be addressed by zero or more | 108 * with another manager. A target manager may be addressed by zero or more |
109 * [_ManagerStub]s. | 109 * [_ManagerStub]s. |
110 * | 110 * TODO(ahe): The _ManagerStub concept is broken. It was an attempt |
111 * to create a common interface between the native Worker class and | |
112 * _MainManagerStub. | |
111 */ | 113 */ |
112 | 114 |
113 /** | 115 /** |
114 * A native object that is shared across isolates. This object is visible to all | 116 * A native object that is shared across isolates. This object is visible to all |
115 * isolates running under the same manager (either UI or background web worker). | 117 * isolates running under the same manager (either UI or background web worker). |
116 * | 118 * |
117 * This is code that is intended to 'escape' the isolate boundaries in order to | 119 * This is code that is intended to 'escape' the isolate boundaries in order to |
118 * implement the semantics of isolates in JavaScript. Without this we would have | 120 * implement the semantics of isolates in JavaScript. Without this we would have |
119 * been forced to implement more code (including the top-level event loop) in | 121 * been forced to implement more code (including the top-level event loop) in |
120 * JavaScript itself. | 122 * JavaScript itself. |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
176 bool get needSerialization => useWorkers; | 178 bool get needSerialization => useWorkers; |
177 | 179 |
178 /** | 180 /** |
179 * Registry of isolates. Isolates must be registered if, and only if, receive | 181 * Registry of isolates. Isolates must be registered if, and only if, receive |
180 * ports are alive. Normally no open receive-ports means that the isolate is | 182 * ports are alive. Normally no open receive-ports means that the isolate is |
181 * dead, but DOM callbacks could resurrect it. | 183 * dead, but DOM callbacks could resurrect it. |
182 */ | 184 */ |
183 Map<int, _IsolateContext> isolates; | 185 Map<int, _IsolateContext> isolates; |
184 | 186 |
185 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ | 187 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ |
186 _ManagerStub mainManager; | 188 _MainManagerStub mainManager; |
187 | 189 |
188 /** Registry of active [_ManagerStub]s. Only used in the main [_Manager]. */ | 190 /// Registry of active Web Workers. Only used in the main [_Manager]. |
189 Map<int, _ManagerStub> managers; | 191 Map<int, dynamic /* Worker */> managers; |
190 | 192 |
191 /** The entry point given by [startRootIsolate]. */ | 193 /** The entry point given by [startRootIsolate]. */ |
192 final Function entry; | 194 final Function entry; |
193 | 195 |
194 _Manager(this.entry) { | 196 _Manager(this.entry) { |
195 _nativeDetectEnvironment(); | 197 _nativeDetectEnvironment(); |
196 topEventLoop = new _EventLoop(); | 198 topEventLoop = new _EventLoop(); |
197 isolates = new Map<int, _IsolateContext>(); | 199 isolates = new Map<int, _IsolateContext>(); |
198 managers = new Map<int, _ManagerStub>(); | 200 managers = new Map<int, dynamic>(); |
199 if (isWorker) { // "if we are not the main manager ourself" is the intent. | 201 if (isWorker) { // "if we are not the main manager ourself" is the intent. |
200 mainManager = new _MainManagerStub(); | 202 mainManager = new _MainManagerStub(); |
201 _nativeInitWorkerMessageHandler(); | 203 _nativeInitWorkerMessageHandler(); |
202 } | 204 } |
203 } | 205 } |
204 | 206 |
205 void _nativeDetectEnvironment() { | 207 void _nativeDetectEnvironment() { |
206 bool isWindowDefined = globalWindow != null; | 208 bool isWindowDefined = globalWindow != null; |
207 bool isWorkerDefined = globalWorker != null; | 209 bool isWorkerDefined = globalWorker != null; |
208 | 210 |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
383 Function fn; | 385 Function fn; |
384 String message; | 386 String message; |
385 | 387 |
386 _IsolateEvent(this.isolate, this.fn, this.message); | 388 _IsolateEvent(this.isolate, this.fn, this.message); |
387 | 389 |
388 void process() { | 390 void process() { |
389 isolate.eval(fn); | 391 isolate.eval(fn); |
390 } | 392 } |
391 } | 393 } |
392 | 394 |
393 /** An interface for a stub used to interact with a manager. */ | |
394 abstract class _ManagerStub { | |
395 get id; | |
396 void set id(int i); | |
397 void set onmessage(Function f); | |
398 void postMessage(msg); | |
399 void terminate(); | |
400 } | |
401 | |
402 /** A stub for interacting with the main manager. */ | 395 /** A stub for interacting with the main manager. */ |
403 class _MainManagerStub implements _ManagerStub { | 396 class _MainManagerStub { |
404 get id => 0; | 397 void postMessage(msg) { |
405 void set id(int i) { throw new UnimplementedError(); } | 398 JS("void", r"self.postMessage(#)", msg); |
ngeoffray
2013/05/15 08:33:10
What's 'self'?
ahe
2013/05/15 09:06:30
self is a way to refer to the global context objec
ngeoffray
2013/05/15 09:09:56
Thanks for the explanation, could you add it to th
ahe
2013/05/15 09:19:08
I'd like to get rid of globalThis, and I'd like to
| |
406 void set onmessage(f) { | |
407 throw new Exception("onmessage should not be set on MainManagerStub"); | |
408 } | 399 } |
409 void postMessage(msg) { | |
410 JS("void", r"#.postMessage(#)", globalThis, msg); | |
411 } | |
412 void terminate() {} // Nothing useful to do here. | |
413 } | |
414 | |
415 /** | |
416 * A stub for interacting with a manager built on a web worker. This | |
417 * definition uses a 'hidden' type (* prefix on the native name) to | |
418 * enforce that the type is defined dynamically only when web workers | |
419 * are actually available. | |
420 */ | |
421 // @Native("*Worker"); | |
422 class _WorkerStub implements _ManagerStub { | |
423 get id => JS("", "#.id", this); | |
424 void set id(i) { JS("void", "#.id = #", this, i); } | |
425 void set onmessage(f) { JS("void", "#.onmessage = #", this, f); } | |
426 void postMessage(msg) { JS("void", "#.postMessage(#)", this, msg); } | |
427 void terminate() { JS("void", "#.terminate()", this); } | |
428 } | 400 } |
429 | 401 |
430 const String _SPAWNED_SIGNAL = "spawned"; | 402 const String _SPAWNED_SIGNAL = "spawned"; |
431 | 403 |
432 var globalThis = IsolateNatives.computeGlobalThis(); | 404 var globalThis = IsolateNatives.computeGlobalThis(); |
433 var globalWindow = JS('', "#.window", globalThis); | 405 var globalWindow = JS('', "#.window", globalThis); |
434 var globalWorker = JS('', "#.Worker", globalThis); | 406 var globalWorker = JS('', "#.Worker", globalThis); |
435 bool globalPostMessageDefined = | 407 bool globalPostMessageDefined = |
436 JS('', "#.postMessage !== (void 0)", globalThis); | 408 JS('', "#.postMessage !== (void 0)", globalThis); |
437 | 409 |
438 class IsolateNatives { | 410 class IsolateNatives { |
439 | 411 |
440 static String thisScript = computeThisScript(); | 412 static String thisScript = computeThisScript(); |
441 | 413 |
414 /// Associates an ID with a native worker object. | |
415 static final Expando<int> workerIds = new Expando<int>(); | |
416 | |
442 /** | 417 /** |
443 * The src url for the script tag that loaded this code. Used to create | 418 * The src url for the script tag that loaded this code. Used to create |
444 * JavaScript workers. | 419 * JavaScript workers. |
445 */ | 420 */ |
446 static String computeThisScript() { | 421 static String computeThisScript() { |
447 var currentScript = JS('', r'$.$currentScript'); | 422 var currentScript = JS('', r'$.$currentScript'); |
448 if (currentScript != null) { | 423 if (currentScript != null) { |
449 return JS('String', 'String(#.src)', currentScript); | 424 return JS('String', 'String(#.src)', currentScript); |
450 } | 425 } |
451 | 426 |
(...skipping 29 matching lines...) Expand all Loading... | |
481 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); | 456 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); |
482 | 457 |
483 matches = JS('=List|Null', '#.match(#)', stack, pattern); | 458 matches = JS('=List|Null', '#.match(#)', stack, pattern); |
484 if (matches != null) return JS('String', '#[1]', matches); | 459 if (matches != null) return JS('String', '#[1]', matches); |
485 | 460 |
486 throw new UnsupportedError('Cannot extract URI from "$stack"'); | 461 throw new UnsupportedError('Cannot extract URI from "$stack"'); |
487 } | 462 } |
488 | 463 |
489 static computeGlobalThis() => JS('', 'function() { return this; }()'); | 464 static computeGlobalThis() => JS('', 'function() { return this; }()'); |
490 | 465 |
491 /** Starts a new worker with the given URL. */ | |
492 static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url) ; | |
493 | |
494 /** | 466 /** |
495 * Assume that [e] is a browser message event and extract its message data. | 467 * Assume that [e] is a browser message event and extract its message data. |
496 * We don't import the dom explicitly so, when workers are disabled, this | 468 * We don't import the dom explicitly so, when workers are disabled, this |
497 * library can also run on top of nodejs. | 469 * library can also run on top of nodejs. |
498 */ | 470 */ |
499 static _getEventData(e) => JS("", "#.data", e); | 471 static _getEventData(e) => JS("", "#.data", e); |
500 | 472 |
501 /** | 473 /** |
502 * Process messages on a worker, either to control the worker instance or to | 474 * Process messages on a worker, either to control the worker instance or to |
503 * pass messages along to the isolate running in the worker. | 475 * pass messages along to the isolate running in the worker. |
504 */ | 476 */ |
505 static void _processWorkerMessage(sender, e) { | 477 static void _processWorkerMessage(/* Worker */ sender, e) { |
506 var msg = _deserializeMessage(_getEventData(e)); | 478 var msg = _deserializeMessage(_getEventData(e)); |
507 switch (msg['command']) { | 479 switch (msg['command']) { |
508 case 'start': | 480 case 'start': |
509 _globalState.currentManagerId = msg['id']; | 481 _globalState.currentManagerId = msg['id']; |
510 String functionName = msg['functionName']; | 482 String functionName = msg['functionName']; |
511 Function entryPoint = (functionName == null) | 483 Function entryPoint = (functionName == null) |
512 ? _globalState.entry | 484 ? _globalState.entry |
513 : _getJSFunctionFromName(functionName); | 485 : _getJSFunctionFromName(functionName); |
514 var replyTo = _deserializeMessage(msg['replyTo']); | 486 var replyTo = _deserializeMessage(msg['replyTo']); |
515 var context = new _IsolateContext(); | 487 var context = new _IsolateContext(); |
(...skipping 14 matching lines...) Expand all Loading... | |
530 break; | 502 break; |
531 case 'message': | 503 case 'message': |
532 SendPort port = msg['port']; | 504 SendPort port = msg['port']; |
533 // If the port has been closed, we ignore the message. | 505 // If the port has been closed, we ignore the message. |
534 if (port != null) { | 506 if (port != null) { |
535 msg['port'].send(msg['msg'], msg['replyTo']); | 507 msg['port'].send(msg['msg'], msg['replyTo']); |
536 } | 508 } |
537 _globalState.topEventLoop.run(); | 509 _globalState.topEventLoop.run(); |
538 break; | 510 break; |
539 case 'close': | 511 case 'close': |
540 _log("Closing Worker"); | 512 _globalState.managers.remove(workerIds[sender]); |
541 _globalState.managers.remove(sender.id); | 513 JS('void', '#.terminate()', sender); |
542 sender.terminate(); | |
543 _globalState.topEventLoop.run(); | 514 _globalState.topEventLoop.run(); |
544 break; | 515 break; |
545 case 'log': | 516 case 'log': |
546 _log(msg['msg']); | 517 _log(msg['msg']); |
547 break; | 518 break; |
548 case 'print': | 519 case 'print': |
549 if (_globalState.isWorker) { | 520 if (_globalState.isWorker) { |
550 _globalState.mainManager.postMessage( | 521 _globalState.mainManager.postMessage( |
551 _serializeMessage({'command': 'print', 'msg': msg})); | 522 _serializeMessage({'command': 'print', 'msg': msg})); |
552 } else { | 523 } else { |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
668 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 639 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
669 topLevel(); | 640 topLevel(); |
670 } | 641 } |
671 | 642 |
672 /** | 643 /** |
673 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor | 644 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |
674 * name for the isolate entry point class. | 645 * name for the isolate entry point class. |
675 */ | 646 */ |
676 static void _spawnWorker(functionName, uri, replyPort) { | 647 static void _spawnWorker(functionName, uri, replyPort) { |
677 if (uri == null) uri = thisScript; | 648 if (uri == null) uri = thisScript; |
678 final worker = _newWorker(uri); | 649 final worker = JS('var', 'new Worker(#)', uri); |
679 worker.onmessage = JS('', | 650 |
680 'function(e) { #(#, e); }', | 651 var processWorkerMessageTrampoline = |
681 DART_CLOSURE_TO_JS(_processWorkerMessage), | 652 JS('', 'function(e) { #(#, e); }', |
682 worker); | 653 DART_CLOSURE_TO_JS(_processWorkerMessage), |
654 worker); | |
655 JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); | |
683 var workerId = _globalState.nextManagerId++; | 656 var workerId = _globalState.nextManagerId++; |
684 // We also store the id on the worker itself so that we can unregister it. | 657 // We also store the id on the worker itself so that we can unregister it. |
ngeoffray
2013/05/15 08:33:10
Update comment?
ahe
2013/05/15 09:06:30
I actually think the comment still applies, I'm ju
ngeoffray
2013/05/15 09:09:56
Fine by me.
| |
685 worker.id = workerId; | 658 workerIds[worker] = workerId; |
686 _globalState.managers[workerId] = worker; | 659 _globalState.managers[workerId] = worker; |
687 worker.postMessage(_serializeMessage({ | 660 JS('void', '#.postMessage(#)', worker, _serializeMessage({ |
688 'command': 'start', | 661 'command': 'start', |
689 'id': workerId, | 662 'id': workerId, |
690 // Note: we serialize replyPort twice because the child worker needs to | 663 // Note: we serialize replyPort twice because the child worker needs to |
691 // first deserialize the worker id, before it can correctly deserialize | 664 // first deserialize the worker id, before it can correctly deserialize |
692 // the port (port deserialization is sensitive to what is the current | 665 // the port (port deserialization is sensitive to what is the current |
693 // workerId). | 666 // workerId). |
694 'replyTo': _serializeMessage(replyPort), | 667 'replyTo': _serializeMessage(replyPort), |
695 'functionName': functionName })); | 668 'functionName': functionName })); |
696 } | 669 } |
697 } | 670 } |
698 | 671 |
699 /******************************************************** | 672 /******************************************************** |
700 Inserted from lib/isolate/dart2js/ports.dart | 673 Inserted from lib/isolate/dart2js/ports.dart |
701 ********************************************************/ | 674 ********************************************************/ |
702 | 675 |
703 /** Common functionality to all send ports. */ | 676 /** Common functionality to all send ports. */ |
704 class _BaseSendPort implements SendPort { | 677 class _BaseSendPort implements SendPort { |
705 /** Id for the destination isolate. */ | 678 /** Id for the destination isolate. */ |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
800 'port': this, | 773 'port': this, |
801 'msg': message, | 774 'msg': message, |
802 'replyTo': replyTo}); | 775 'replyTo': replyTo}); |
803 | 776 |
804 if (_globalState.isWorker) { | 777 if (_globalState.isWorker) { |
805 // Communication from one worker to another go through the | 778 // Communication from one worker to another go through the |
806 // main worker. | 779 // main worker. |
807 _globalState.mainManager.postMessage(workerMessage); | 780 _globalState.mainManager.postMessage(workerMessage); |
808 } else { | 781 } else { |
809 // Deliver the message only if the worker is still alive. | 782 // Deliver the message only if the worker is still alive. |
810 _ManagerStub manager = _globalState.managers[_workerId]; | 783 /* Worker */ var manager = _globalState.managers[_workerId]; |
811 if (manager != null) { | 784 if (manager != null) { |
812 manager.postMessage(workerMessage); | 785 JS('void', '#.postMessage(#)', manager, workerMessage); |
813 } | 786 } |
814 } | 787 } |
815 }); | 788 }); |
816 } | 789 } |
817 | 790 |
818 bool operator ==(var other) { | 791 bool operator ==(var other) { |
819 return (other is _WorkerSendPort) && | 792 return (other is _WorkerSendPort) && |
820 (_workerId == other._workerId) && | 793 (_workerId == other._workerId) && |
821 (_isolateId == other._isolateId) && | 794 (_isolateId == other._isolateId) && |
822 (_receivePortId == other._receivePortId); | 795 (_receivePortId == other._receivePortId); |
(...skipping 608 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1431 JS('void', '#.clearInterval(#)', globalThis, _handle); | 1404 JS('void', '#.clearInterval(#)', globalThis, _handle); |
1432 } | 1405 } |
1433 _handle = null; | 1406 _handle = null; |
1434 } else { | 1407 } else { |
1435 throw new UnsupportedError("Canceling a timer."); | 1408 throw new UnsupportedError("Canceling a timer."); |
1436 } | 1409 } |
1437 } | 1410 } |
1438 } | 1411 } |
1439 | 1412 |
1440 bool hasTimer() => JS('', '#.setTimeout', globalThis) != null; | 1413 bool hasTimer() => JS('', '#.setTimeout', globalThis) != null; |
OLD | NEW |