Chromium Code Reviews| 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 |