| OLD | NEW |
| 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 /** | 5 /** |
| 6 * A native object that is shared across isolates. This object is visible to all | 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). | 7 * isolates running on the same worker (either UI or background web worker). |
| 8 * | 8 * |
| 9 * This is code that is intended to 'escape' the isolate boundaries in order to | 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 | 10 * implement the semantics of friendly isolates in JavaScript. Without this we |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 155 | 155 |
| 156 _deserializeMessage(message) { | 156 _deserializeMessage(message) { |
| 157 if (_globalState.needSerialization) { | 157 if (_globalState.needSerialization) { |
| 158 return new Deserializer().deserialize(message); | 158 return new Deserializer().deserialize(message); |
| 159 } else { | 159 } else { |
| 160 // Nothing more to do. | 160 // Nothing more to do. |
| 161 return message; | 161 return message; |
| 162 } | 162 } |
| 163 } | 163 } |
| 164 | 164 |
| 165 /** Wait until all ports in a message are resolved. */ |
| 166 _waitForPendingPorts(var message, void callback()) { |
| 167 final finder = new PendingSendPortFinder(); |
| 168 finder.traverse(message); |
| 169 Futures.wait(finder.ports).then((_) => callback()); |
| 170 } |
| 171 |
| 165 /** Default worker. */ | 172 /** Default worker. */ |
| 166 class MainWorker { | 173 class MainWorker { |
| 167 int id = 0; | 174 int id = 0; |
| 168 void postMessage(msg) native "return \$globalThis.postMessage(msg);"; | 175 void postMessage(msg) native "return \$globalThis.postMessage(msg);"; |
| 169 void set onmessage(f) native "\$globalThis.onmessage = f;"; | 176 void set onmessage(f) native "\$globalThis.onmessage = f;"; |
| 170 void terminate() {} | 177 void terminate() {} |
| 171 } | 178 } |
| 172 | 179 |
| 173 /** | 180 /** |
| 174 * A web worker. This type is also defined in 'dart:dom', but we define it here | 181 * A web worker. This type is also defined in 'dart:dom', but we define it here |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 Function fn; | 326 Function fn; |
| 320 String message; | 327 String message; |
| 321 | 328 |
| 322 IsolateEvent(this.isolate, this.fn, this.message); | 329 IsolateEvent(this.isolate, this.fn, this.message); |
| 323 | 330 |
| 324 void process() { | 331 void process() { |
| 325 isolate.eval(fn); | 332 isolate.eval(fn); |
| 326 } | 333 } |
| 327 } | 334 } |
| 328 | 335 |
| 329 /** Implementation of a send port on top of JavaScript. */ | 336 /** Common functionality to all send ports. */ |
| 330 class SendPortImpl implements SendPort { | 337 class BaseSendPort implements SendPort { |
| 338 final int _isolateId; |
| 331 | 339 |
| 332 const SendPortImpl(this._workerId, this._isolateId, this._receivePortId); | 340 BaseSendPort(this._isolateId); |
| 333 | |
| 334 void send(var message, [SendPort replyTo = null]) { | |
| 335 if (replyTo !== null && !(replyTo is SendPortImpl)) { | |
| 336 throw "SendPort::send: Illegal replyTo type."; | |
| 337 } | |
| 338 IsolateNatives._sendMessage(_workerId, _isolateId, _receivePortId, | |
| 339 _serializeMessage(message), _serializeMessage(replyTo)); | |
| 340 } | |
| 341 | |
| 342 // TODO(sigmund): get rid of _sendNow (still used in corelib code) | |
| 343 void _sendNow(var message, replyTo) { send(message, replyTo); } | |
| 344 | 341 |
| 345 ReceivePortSingleShotImpl call(var message) { | 342 ReceivePortSingleShotImpl call(var message) { |
| 346 final result = new ReceivePortSingleShotImpl(); | 343 final result = new ReceivePortSingleShotImpl(); |
| 347 this.send(message, result.toSendPort()); | 344 this.send(message, result.toSendPort()); |
| 348 return result; | 345 return result; |
| 349 } | 346 } |
| 350 | 347 |
| 351 ReceivePortSingleShotImpl _callNow(var message) { | 348 static void checkReplyTo(SendPort replyTo) { |
| 352 final result = new ReceivePortSingleShotImpl(); | 349 if (replyTo !== null |
| 353 send(message, result.toSendPort()); | 350 && replyTo is! NativeJsSendPort |
| 354 return result; | 351 && replyTo is! WorkerSendPort |
| 352 && replyTo is! BufferingSendPort) { |
| 353 throw new Exception("SendPort.send: Illegal replyTo port type"); |
| 354 } |
| 355 } | 355 } |
| 356 | 356 |
| 357 bool operator==(var other) { | 357 // TODO(sigmund): replace the current SendPort.call with the following: |
| 358 return (other is SendPortImpl) && | 358 //Future call(var message) { |
| 359 // final completer = new Completer(); |
| 360 // final port = new ReceivePort.singleShot(); |
| 361 // send(message, port.toSendPort()); |
| 362 // port.receive((value, ignoreReplyTo) { |
| 363 // if (value is Exception) { |
| 364 // completer.completeException(value); |
| 365 // } else { |
| 366 // completer.complete(value); |
| 367 // } |
| 368 // }); |
| 369 // return completer.future; |
| 370 //} |
| 371 |
| 372 abstract void send(var message, [SendPort replyTo]); |
| 373 abstract bool operator ==(var other); |
| 374 abstract int hashCode(); |
| 375 } |
| 376 |
| 377 /** A send port that delivers messages in-memory via native JavaScript calls. */ |
| 378 class NativeJsSendPort extends BaseSendPort implements SendPort { |
| 379 final ReceivePortImpl _receivePort; |
| 380 |
| 381 const NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId); |
| 382 |
| 383 void send(var message, [SendPort replyTo = null]) { |
| 384 _waitForPendingPorts([message, replyTo], () { |
| 385 checkReplyTo(replyTo); |
| 386 // Check that the isolate still runs and the port is still open |
| 387 final isolate = _globalState.isolates[_isolateId]; |
| 388 if (isolate == null) return; |
| 389 if (_receivePort._callback == null) return; |
| 390 |
| 391 // messages from WorkerSendPorts get forwarded by [_processWorkerMessage], |
| 392 // in that case we no isolate is currently active, and the message was |
| 393 // already serialized and deserialized. |
| 394 final shouldSerialize = _globalState.currentContext == null; |
| 395 _globalState.topEventLoop.enqueue(isolate, () { |
| 396 if (_receivePort._callback != null) { |
| 397 if (shouldSerialize) { |
| 398 // Force serialization/deserialization as a simple way to ensure |
| 399 // isolate communication restrictions are respected. |
| 400 message = _deserializeMessage(_serializeMessage(message)); |
| 401 replyTo = _deserializeMessage(_serializeMessage(replyTo)); |
| 402 } |
| 403 _receivePort._callback(message, replyTo); |
| 404 } |
| 405 }, 'receive ' + message); |
| 406 }); |
| 407 } |
| 408 |
| 409 bool operator ==(var other) => (other is NativeJsSendPort) && |
| 410 (_receivePort == other._receivePort); |
| 411 |
| 412 int hashCode() => _receivePort._id; |
| 413 } |
| 414 |
| 415 /** A send port that delivers messages via worker.postMessage. */ |
| 416 class WorkerSendPort extends BaseSendPort implements SendPort { |
| 417 final int _workerId; |
| 418 final int _receivePortId; |
| 419 |
| 420 const WorkerSendPort(this._workerId, int isolateId, this._receivePortId) |
| 421 : super(isolateId); |
| 422 |
| 423 void send(var message, [SendPort replyTo = null]) { |
| 424 _waitForPendingPorts([message, replyTo], () { |
| 425 checkReplyTo(replyTo); |
| 426 final workerMessage = _serializeMessage({ |
| 427 'command': 'message', |
| 428 'port': _serializeMessage(this), |
| 429 'msg': message, |
| 430 'replyTo': _serializeMessage(replyTo)}); |
| 431 |
| 432 if (_globalState.isWorker) { |
| 433 // communication from one worker to another go through the main worker: |
| 434 _globalState.mainWorker.postMessage(workerMessage); |
| 435 } else { |
| 436 _globalState.workers[_workerId].postMessage(workerMessage); |
| 437 } |
| 438 }); |
| 439 } |
| 440 |
| 441 bool operator ==(var other) { |
| 442 return (other is WorkerSendPort) && |
| 359 (_workerId == other._workerId) && | 443 (_workerId == other._workerId) && |
| 360 (_isolateId == other._isolateId) && | 444 (_isolateId == other._isolateId) && |
| 361 (_receivePortId == other._receivePortId); | 445 (_receivePortId == other._receivePortId); |
| 362 } | 446 } |
| 363 | 447 |
| 364 int hashCode() { | 448 int hashCode() { |
| 365 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; | 449 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; |
| 366 } | 450 } |
| 451 } |
| 367 | 452 |
| 368 final int _receivePortId; | 453 /** A port that buffers messages until an underlying port gets resolve. */ |
| 369 final int _isolateId; | 454 class BufferingSendPort extends BaseSendPort implements SendPort { |
| 370 final int _workerId; | 455 static int _bufferingCount = 0; |
| 456 |
| 457 /** For implementing equals and hashcode. */ |
| 458 final int id; |
| 459 |
| 460 /** Underlying port, when resolved. */ |
| 461 SendPort _port; |
| 462 |
| 463 /** |
| 464 * Future of the underlying port, so that we can detect when this port can be |
| 465 * sent on messages. |
| 466 */ |
| 467 Future<SendPort> _futurePort; |
| 468 |
| 469 /** Pending messages (and reply ports). */ |
| 470 List pending; |
| 471 |
| 472 BufferingSendPort(isolateId, this._futurePort) |
| 473 : super(isolateId), id = _bufferingCount, pending = [] { |
| 474 _bufferingCount++; |
| 475 _futurePort.then((p) { |
| 476 _port = p; |
| 477 for (final message in pending) { |
| 478 p.send(message[0], message[1]); |
| 479 } |
| 480 pending = null; |
| 481 }); |
| 482 } |
| 483 |
| 484 BufferingSendPort.fromPort(isolateId, this._port) |
| 485 : super(isolateId), id = _bufferingCount { |
| 486 _bufferingCount++; |
| 487 } |
| 488 |
| 489 void send(var message, [SendPort replyTo]) { |
| 490 if (_port != null) { |
| 491 _port.send(message, replyTo); |
| 492 } else { |
| 493 pending.add([message, replyTo]); |
| 494 } |
| 495 } |
| 496 |
| 497 bool operator ==(var other) => (other is BufferingSendPort && id == other.id); |
| 498 int hashCode() => id; |
| 371 } | 499 } |
| 372 | 500 |
| 373 /** Default factory for receive ports. */ | 501 /** Default factory for receive ports. */ |
| 374 class ReceivePortFactory { | 502 class ReceivePortFactory { |
| 375 | 503 |
| 376 factory ReceivePort() { | 504 factory ReceivePort() { |
| 377 return new ReceivePortImpl(); | 505 return new ReceivePortImpl(); |
| 378 } | 506 } |
| 379 | 507 |
| 380 factory ReceivePort.singleShot() { | 508 factory ReceivePort.singleShot() { |
| 381 return new ReceivePortSingleShotImpl(); | 509 return new ReceivePortSingleShotImpl(); |
| 382 } | 510 } |
| 383 } | 511 } |
| 384 | 512 |
| 385 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ | 513 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ |
| 386 class ReceivePortImpl implements ReceivePort { | 514 class ReceivePortImpl implements ReceivePort { |
| 515 int _id; |
| 516 Function _callback; |
| 517 static int _nextFreeId = 1; |
| 518 |
| 387 ReceivePortImpl() | 519 ReceivePortImpl() |
| 388 : _id = _nextFreeId++ { | 520 : _id = _nextFreeId++ { |
| 389 _globalState.currentContext.register(_id, this); | 521 _globalState.currentContext.register(_id, this); |
| 390 } | 522 } |
| 391 | 523 |
| 392 void receive(void onMessage(var message, SendPort replyTo)) { | 524 void receive(void onMessage(var message, SendPort replyTo)) { |
| 393 _callback = onMessage; | 525 _callback = onMessage; |
| 394 } | 526 } |
| 395 | 527 |
| 396 void close() { | 528 void close() { |
| 397 _callback = null; | 529 _callback = null; |
| 398 _globalState.currentContext.unregister(_id); | 530 _globalState.currentContext.unregister(_id); |
| 399 } | 531 } |
| 400 | 532 |
| 401 /** | |
| 402 * Returns a fresh [SendPort]. The implementation is not allowed to cache | |
| 403 * existing ports. | |
| 404 */ | |
| 405 SendPort toSendPort() { | 533 SendPort toSendPort() { |
| 406 return new SendPortImpl( | 534 return new NativeJsSendPort(this, _globalState.currentContext.id); |
| 407 _globalState.currentWorkerId, _globalState.currentContext.id, _id); | |
| 408 } | 535 } |
| 409 | |
| 410 int _id; | |
| 411 Function _callback; | |
| 412 | |
| 413 static int _nextFreeId = 1; | |
| 414 } | 536 } |
| 415 | 537 |
| 416 /** Implementation of a single-shot [ReceivePort]. */ | 538 /** Implementation of a single-shot [ReceivePort]. */ |
| 417 class ReceivePortSingleShotImpl implements ReceivePort { | 539 class ReceivePortSingleShotImpl implements ReceivePort { |
| 418 | 540 |
| 419 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } | 541 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } |
| 420 | 542 |
| 421 void receive(void callback(var message, SendPort replyTo)) { | 543 void receive(void callback(var message, SendPort replyTo)) { |
| 422 _port.receive((var message, SendPort replyTo) { | 544 _port.receive((var message, SendPort replyTo) { |
| 423 _port.close(); | 545 _port.close(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 | 579 |
| 458 return completer.future; | 580 return completer.future; |
| 459 } | 581 } |
| 460 | 582 |
| 461 static SendPort _startWorker(Isolate runnable, SendPort replyPort) { | 583 static SendPort _startWorker(Isolate runnable, SendPort replyPort) { |
| 462 var factoryName = _getJSConstructorName(runnable); | 584 var factoryName = _getJSConstructorName(runnable); |
| 463 if (_globalState.isWorker) { | 585 if (_globalState.isWorker) { |
| 464 _globalState.mainWorker.postMessage(_serializeMessage({ | 586 _globalState.mainWorker.postMessage(_serializeMessage({ |
| 465 'command': 'spawn-worker', | 587 'command': 'spawn-worker', |
| 466 'factoryName': factoryName, | 588 'factoryName': factoryName, |
| 467 'replyPort': replyPort})); | 589 'replyPort': _serializeMessage(replyPort)})); |
| 468 } else { | 590 } else { |
| 469 _spawnWorker(factoryName, _serializeMessage(replyPort)); | 591 _spawnWorker(factoryName, _serializeMessage(replyPort)); |
| 470 } | 592 } |
| 471 } | 593 } |
| 472 | 594 |
| 473 | |
| 474 /** | 595 /** |
| 475 * The src url for the script tag that loaded this code. Used to create | 596 * The src url for the script tag that loaded this code. Used to create |
| 476 * JavaScript workers. | 597 * JavaScript workers. |
| 477 */ | 598 */ |
| 478 static String get _thisScript() => | 599 static String get _thisScript() => |
| 479 _thisScriptCache != null ? _thisScriptCache : _computeThisScript(); | 600 _thisScriptCache != null ? _thisScriptCache : _computeThisScript(); |
| 480 | 601 |
| 481 static String _thisScriptCache; | 602 static String _thisScriptCache; |
| 482 | 603 |
| 483 // TODO(sigmund): fix - this code should be run synchronously when loading the | 604 // TODO(sigmund): fix - this code should be run synchronously when loading the |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 529 */ | 650 */ |
| 530 static _getEventData(e) native "return e.data"; | 651 static _getEventData(e) native "return e.data"; |
| 531 | 652 |
| 532 /** | 653 /** |
| 533 * Process messages on a worker, either to control the worker instance or to | 654 * Process messages on a worker, either to control the worker instance or to |
| 534 * pass messages along to the isolate running in the worker. | 655 * pass messages along to the isolate running in the worker. |
| 535 */ | 656 */ |
| 536 static void _processWorkerMessage(sender, e) { | 657 static void _processWorkerMessage(sender, e) { |
| 537 var msg = _deserializeMessage(_getEventData(e)); | 658 var msg = _deserializeMessage(_getEventData(e)); |
| 538 switch (msg['command']) { | 659 switch (msg['command']) { |
| 660 // TODO(sigmund): delete after we migrate to Isolate2 |
| 539 case 'start': | 661 case 'start': |
| 540 _globalState.currentWorkerId = msg['id']; | 662 _globalState.currentWorkerId = msg['id']; |
| 541 var runnerObject = | 663 var runnerObject = |
| 542 _allocate(_getJSConstructorFromName(msg['factoryName'])); | 664 _allocate(_getJSConstructorFromName(msg['factoryName'])); |
| 543 var serializedReplyTo = msg['replyTo']; | 665 var serializedReplyTo = msg['replyTo']; |
| 544 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { | 666 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { |
| 545 var replyTo = _deserializeMessage(serializedReplyTo); | 667 var replyTo = _deserializeMessage(serializedReplyTo); |
| 546 IsolateNatives._startIsolate(runnerObject, replyTo); | 668 _startIsolate(runnerObject, replyTo); |
| 547 }, 'worker-start'); | 669 }, 'worker-start'); |
| 548 _globalState.topEventLoop.run(); | 670 _globalState.topEventLoop.run(); |
| 549 break; | 671 break; |
| 672 case 'start2': |
| 673 _globalState.currentWorkerId = msg['id']; |
| 674 Function entryPoint = _getJSFunctionFromName(msg['functionName']); |
| 675 var replyTo = _deserializeMessage(msg['replyTo']); |
| 676 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { |
| 677 _startIsolate2(entryPoint, replyTo); |
| 678 }, 'worker-start'); |
| 679 _globalState.topEventLoop.run(); |
| 680 break; |
| 681 // TODO(sigmund): delete after we migrate to Isolate2 |
| 550 case 'spawn-worker': | 682 case 'spawn-worker': |
| 551 _spawnWorker(msg['factoryName'], msg['replyPort']); | 683 _spawnWorker(msg['factoryName'], msg['replyPort']); |
| 552 break; | 684 break; |
| 685 case 'spawn-worker2': |
| 686 _spawnWorker2(msg['functionName'], msg['uri'], msg['replyPort']); |
| 687 break; |
| 553 case 'message': | 688 case 'message': |
| 554 _sendMessage(msg['workerId'], msg['isolateId'], msg['portId'], | 689 final iid = _globalState.currentContext == null ? '?' : '${_globalState.
currentContext.id }'; |
| 555 msg['msg'], msg['replyTo']); | 690 final port = _deserializeMessage(msg['port']); |
| 691 port.send(msg['msg'], _deserializeMessage(msg['replyTo'])); |
| 556 _globalState.topEventLoop.run(); | 692 _globalState.topEventLoop.run(); |
| 557 break; | 693 break; |
| 558 case 'close': | 694 case 'close': |
| 559 _log("Closing Worker"); | 695 _log("Closing Worker"); |
| 560 _globalState.workers.remove(sender.id); | 696 _globalState.workers.remove(sender.id); |
| 561 sender.terminate(); | 697 sender.terminate(); |
| 562 _globalState.topEventLoop.run(); | 698 _globalState.topEventLoop.run(); |
| 563 break; | 699 break; |
| 564 case 'log': | 700 case 'log': |
| 565 _log(msg['msg']); | 701 _log(msg['msg']); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 606 // TODO(sigmund): find a browser-generic way to support this. | 742 // TODO(sigmund): find a browser-generic way to support this. |
| 607 static var _getJSConstructorName(Isolate runnable) native """ | 743 static var _getJSConstructorName(Isolate runnable) native """ |
| 608 return runnable.constructor.name; | 744 return runnable.constructor.name; |
| 609 """; | 745 """; |
| 610 | 746 |
| 611 /** Find a constructor given it's name. */ | 747 /** Find a constructor given it's name. */ |
| 612 static var _getJSConstructorFromName(String factoryName) native """ | 748 static var _getJSConstructorFromName(String factoryName) native """ |
| 613 return \$globalThis[factoryName]; | 749 return \$globalThis[factoryName]; |
| 614 """; | 750 """; |
| 615 | 751 |
| 752 static var _getJSFunctionFromName(String functionName) native """ |
| 753 return \$globalThis[functionName]; |
| 754 """; |
| 755 |
| 756 static String _getJSFunctionName(Function f) native "return f.name || null;"; |
| 757 |
| 616 /** Create a new JavasSript object instance given it's constructor. */ | 758 /** Create a new JavasSript object instance given it's constructor. */ |
| 617 static var _allocate(var ctor) native "return new ctor();"; | 759 static var _allocate(var ctor) native "return new ctor();"; |
| 618 | 760 |
| 619 /** Starts a non-worker isolate. */ | 761 /** Starts a non-worker isolate. */ |
| 620 static SendPort _startNonWorker(Isolate runnable, SendPort replyTo) { | 762 static SendPort _startNonWorker(Isolate runnable, SendPort replyTo) { |
| 621 // Spawn a new isolate and create the receive port in it. | 763 // Spawn a new isolate and create the receive port in it. |
| 622 final spawned = new IsolateContext(); | 764 final spawned = new IsolateContext(); |
| 623 | 765 |
| 624 // Instead of just running the provided runnable, we create a | 766 // Instead of just running the provided runnable, we create a |
| 625 // new cloned instance of it with a fresh state in the spawned | 767 // new cloned instance of it with a fresh state in the spawned |
| 626 // isolate. This way, we do not get cross-isolate references | 768 // isolate. This way, we do not get cross-isolate references |
| 627 // through the runnable. | 769 // through the runnable. |
| 628 final ctor = _getJSConstructor(runnable); | 770 final ctor = _getJSConstructor(runnable); |
| 629 _globalState.topEventLoop.enqueue(spawned, function() { | 771 _globalState.topEventLoop.enqueue(spawned, function() { |
| 630 _startIsolate(_allocate(ctor), replyTo); | 772 _startIsolate(_allocate(ctor), replyTo); |
| 631 }, 'nonworker start'); | 773 }, 'nonworker start'); |
| 632 } | 774 } |
| 633 | 775 |
| 634 /** Given a ready-to-start runnable, start running it. */ | 776 /** Given a ready-to-start runnable, start running it. */ |
| 635 static void _startIsolate(Isolate isolate, SendPort replyTo) { | 777 static void _startIsolate(Isolate isolate, SendPort replyTo) { |
| 636 _fillStatics(_globalState.currentContext); | 778 _fillStatics(_globalState.currentContext); |
| 637 ReceivePort port = new ReceivePort(); | 779 ReceivePort port = new ReceivePort(); |
| 638 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 780 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
| 639 isolate._run(port); | 781 isolate._run(port); |
| 640 } | 782 } |
| 641 | 783 |
| 642 static void _sendMessage(int workerId, int isolateId, int receivePortId, | 784 // TODO(sigmund): clean up above, after we make the new API the default: |
| 643 message, replyTo) { | 785 |
| 644 // Both the message and the replyTo are already serialized. | 786 static _spawn2(String functionName, String uri, bool isLight) { |
| 645 if (workerId == _globalState.currentWorkerId) { | 787 Completer<SendPort> completer = new Completer<SendPort>(); |
| 646 var isolate = _globalState.isolates[isolateId]; | 788 ReceivePort port = new ReceivePort.singleShot(); |
| 647 if (isolate == null) return; // Isolate has been closed. | 789 port.receive((msg, SendPort replyPort) { |
| 648 var receivePort = isolate.lookup(receivePortId); | 790 assert(msg == _SPAWNED_SIGNAL); |
| 649 if (receivePort == null) return; // ReceivePort has been closed. | 791 completer.complete(replyPort); |
| 650 _globalState.topEventLoop.enqueue(isolate, () { | 792 }); |
| 651 if (receivePort._callback != null) { | 793 |
| 652 receivePort._callback( | 794 SendPort signalReply = port.toSendPort(); |
| 653 _deserializeMessage(message), _deserializeMessage(replyTo)); | 795 |
| 654 } | 796 if (_globalState.useWorkers && !isLight) { |
| 655 }, 'receive ' + message); | 797 _startWorker2(functionName, uri, signalReply); |
| 656 } else { | 798 } else { |
| 657 var worker; | 799 _startNonWorker2(functionName, uri, signalReply); |
| 658 // communication between workers go through the main worker | 800 } |
| 659 if (_globalState.isWorker) { | 801 return new BufferingSendPort( |
| 660 worker = _globalState.mainWorker; | 802 _globalState.currentContext.id, completer.future); |
| 661 } else { | 803 } |
| 662 // TODO(sigmund): make sure this works | 804 |
| 663 worker = _globalState.workers[workerId]; | 805 static SendPort _startWorker2( |
| 664 } | 806 String functionName, String uri, SendPort replyPort) { |
| 665 worker.postMessage(_serializeMessage({ | 807 if (_globalState.isWorker) { |
| 666 'command': 'message', | 808 _globalState.mainWorker.postMessage(_serializeMessage({ |
| 667 'workerId': workerId, | 809 'command': 'spawn-worker2', |
| 668 'isolateId': isolateId, | 810 'functionName': functionName, |
| 669 'portId': receivePortId, | 811 'uri': uri, |
| 670 'msg': message, | 812 'replyPort': replyPort})); |
| 671 'replyTo': replyTo })); | 813 } else { |
| 814 _spawnWorker2(functionName, uri, replyPort); |
| 672 } | 815 } |
| 673 } | 816 } |
| 817 |
| 818 static SendPort _startNonWorker2( |
| 819 String functionName, String uri, SendPort replyPort) { |
| 820 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { |
| 821 final func = _getJSFunctionFromName(functionName); |
| 822 _startIsolate2(func, replyPort); |
| 823 }, 'nonworker start'); |
| 824 } |
| 825 |
| 826 static void _startIsolate2(Function topLevel, SendPort replyTo) { |
| 827 _fillStatics(_globalState.currentContext); |
| 828 final port = new ReceivePort(); |
| 829 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
| 830 topLevel(port); |
| 831 } |
| 832 |
| 833 /** |
| 834 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |
| 835 * name for the isolate entry point class. |
| 836 */ |
| 837 static void _spawnWorker2(functionName, uri, replyPort) { |
| 838 if (uri == null) uri = _thisScript; |
| 839 final worker = _newWorker(uri); |
| 840 worker.onmessage = (e) { _processWorkerMessage(worker, e); }; |
| 841 var workerId = _globalState.nextWorkerId++; |
| 842 // We also store the id on the worker itself so that we can unregister it. |
| 843 worker.id = workerId; |
| 844 _globalState.workers[workerId] = worker; |
| 845 worker.postMessage(_serializeMessage({ |
| 846 'command': 'start2', |
| 847 'id': workerId, |
| 848 // Note: we serialize replyPort twice because the child worker needs to |
| 849 // first deserialize the worker id, before it can correctly deserialize |
| 850 // the port (port deserialization is sensitive to what is the current |
| 851 // workerId). |
| 852 'replyTo': _serializeMessage(replyPort), |
| 853 'functionName': functionName })); |
| 854 } |
| 674 } | 855 } |
| 856 |
| 857 class Isolate2Impl implements Isolate2 { |
| 858 SendPort sendPort; |
| 859 |
| 860 Isolate2Impl(this.sendPort); |
| 861 |
| 862 void stop() {} |
| 863 } |
| 864 |
| 865 class IsolateFactory implements Isolate2 { |
| 866 |
| 867 factory Isolate2.fromCode(Function topLevelFunction) { |
| 868 final name = IsolateNatives._getJSFunctionName(topLevelFunction); |
| 869 if (name == null) { |
| 870 throw new UnsupportedOperationException( |
| 871 "only top-level functions can be spawned."); |
| 872 } |
| 873 return new Isolate2Impl(IsolateNatives._spawn2(name, null, false)); |
| 874 } |
| 875 |
| 876 factory Isolate2.fromUri(String uri) { |
| 877 return new Isolate2Impl(IsolateNatives._spawn2(null, uri, false)); |
| 878 } |
| 879 } |
| OLD | NEW |