Chromium Code Reviews| 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 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 319 Function fn; | 319 Function fn; |
| 320 String message; | 320 String message; |
| 321 | 321 |
| 322 IsolateEvent(this.isolate, this.fn, this.message); | 322 IsolateEvent(this.isolate, this.fn, this.message); |
| 323 | 323 |
| 324 void process() { | 324 void process() { |
| 325 isolate.eval(fn); | 325 isolate.eval(fn); |
| 326 } | 326 } |
| 327 } | 327 } |
| 328 | 328 |
| 329 /** Implementation of a send port on top of JavaScript. */ | 329 /** Common functionality to all send ports. */ |
| 330 class SendPortImpl implements SendPort { | 330 class BaseSendPort implements SendPort { |
| 331 final int _isolateId; | |
|
eub
2012/02/09 01:13:42
id of destination?
Siggi Cherem (dart-lang)
2012/02/10 00:16:41
Yes. Added a comment.
| |
| 331 | 332 |
| 332 const SendPortImpl(this._workerId, this._isolateId, this._receivePortId); | 333 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 | 334 |
| 345 ReceivePortSingleShotImpl call(var message) { | 335 ReceivePortSingleShotImpl call(var message) { |
| 346 final result = new ReceivePortSingleShotImpl(); | 336 final result = new ReceivePortSingleShotImpl(); |
| 347 this.send(message, result.toSendPort()); | 337 this.send(message, result.toSendPort()); |
| 348 return result; | 338 return result; |
| 349 } | 339 } |
| 350 | 340 |
| 351 ReceivePortSingleShotImpl _callNow(var message) { | 341 static void checkReplyTo(SendPort replyTo) { |
| 352 final result = new ReceivePortSingleShotImpl(); | 342 if (replyTo !== null && replyTo is! NativeJsSendPort |
| 353 send(message, result.toSendPort()); | 343 && replyTo is! WorkerSendPort) { |
| 354 return result; | 344 throw "SendPort.send: Illegal replyTo port type."; |
| 345 } | |
| 355 } | 346 } |
| 356 | 347 |
| 357 bool operator==(var other) { | 348 // TODO(sigmund): replace the current SendPort.call with the following: |
| 358 return (other is SendPortImpl) && | 349 //Future call(var message) { |
| 350 // final completer = new Completer(); | |
| 351 // final port = new ReceivePort.singleShot(); | |
| 352 // send(message, port.toSendPort()); | |
| 353 // port.receive((value, ignoreReplyTo) { | |
| 354 // if (value is Exception) { | |
| 355 // completer.completeException(value); | |
| 356 // } else { | |
| 357 // completer.complete(value); | |
| 358 // } | |
| 359 // }); | |
| 360 // return completer.future; | |
| 361 //} | |
| 362 | |
| 363 abstract void send(var message, [SendPort replyTo]); | |
| 364 abstract bool operator ==(var other); | |
| 365 abstract int hashCode(); | |
| 366 } | |
| 367 | |
| 368 /** A send port that delivers messages in-memory via native JavaScript calls. */ | |
| 369 class NativeJsSendPort extends BaseSendPort implements SendPort { | |
| 370 final ReceivePortImpl _receivePort; | |
| 371 | |
| 372 const NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId); | |
| 373 | |
| 374 void send(var message, [SendPort replyTo = null]) { | |
| 375 checkReplyTo(replyTo); | |
| 376 // Check that the isolate still runs and the port is still open | |
| 377 final isolate = _globalState.isolates[_isolateId]; | |
| 378 if (isolate == null) return; | |
| 379 if (_receivePort._callback == null) return; | |
| 380 | |
| 381 // messages from WorkerSendPorts get forwarded by [_processWorkerMessage], | |
| 382 // in that case we no isolate is currently active, and the message was | |
| 383 // already serialized and deserialized. | |
| 384 final shouldSerialize = _globalState.currentContext == null; | |
|
eub
2012/02/09 01:13:42
(Revisit per our discussion -- is this test backwa
Siggi Cherem (dart-lang)
2012/02/10 00:16:41
cleaned up. Should make more sense now :)
| |
| 385 _globalState.topEventLoop.enqueue(isolate, () { | |
| 386 if (_receivePort._callback != null) { | |
| 387 if (shouldSerialize) { | |
| 388 // Force serialization/deserialization as a simple way to ensure | |
| 389 // isolate communication restrictions are respected. | |
| 390 message = _deserializeMessage(_serializeMessage(message)); | |
| 391 replyTo = _deserializeMessage(_serializeMessage(replyTo)); | |
| 392 } | |
| 393 _receivePort._callback(message, replyTo); | |
| 394 } | |
| 395 }, 'receive ' + message); | |
| 396 } | |
| 397 | |
| 398 bool operator ==(var other) => (other is NativeJsSendPort) && | |
| 399 (_receivePort == other._receivePort); | |
|
eub
2012/02/09 01:13:42
(Side note about Dart: this idiom strikes me as we
Siggi Cherem (dart-lang)
2012/02/10 00:16:41
yeah, it would be nice to have 'final' or 'sealed'
| |
| 400 | |
| 401 int hashCode() => _receivePort._id; | |
| 402 } | |
| 403 | |
| 404 /** A send port that delivers messages via worker.postMessage. */ | |
| 405 class WorkerSendPort extends BaseSendPort implements SendPort { | |
| 406 final int _workerId; | |
| 407 final int _receivePortId; | |
| 408 | |
| 409 const WorkerSendPort(this._workerId, int isolateId, this._receivePortId) | |
| 410 : super(isolateId); | |
| 411 | |
| 412 void send(var message, [SendPort replyTo = null]) { | |
| 413 checkReplyTo(replyTo); | |
| 414 final workerMessage = _serializeMessage({ | |
| 415 'command': 'message', | |
| 416 'port': this, | |
| 417 'msg': message, | |
| 418 'replyTo': replyTo}); | |
| 419 | |
| 420 if (_globalState.isWorker) { | |
| 421 // communication from one worker to another go through the main worker: | |
| 422 _globalState.mainWorker.postMessage(workerMessage); | |
| 423 } else { | |
| 424 _globalState.workers[_workerId].postMessage(workerMessage); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 bool operator ==(var other) { | |
| 429 return (other is WorkerSendPort) && | |
| 359 (_workerId == other._workerId) && | 430 (_workerId == other._workerId) && |
| 360 (_isolateId == other._isolateId) && | 431 (_isolateId == other._isolateId) && |
| 361 (_receivePortId == other._receivePortId); | 432 (_receivePortId == other._receivePortId); |
| 362 } | 433 } |
| 363 | 434 |
| 364 int hashCode() { | 435 int hashCode() { |
| 365 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; | 436 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; |
|
eub
2012/02/09 01:13:42
If we believe these bit windows don't overlap, usi
Siggi Cherem (dart-lang)
2012/02/10 00:16:41
Done.
| |
| 366 } | 437 } |
| 367 | |
| 368 final int _receivePortId; | |
| 369 final int _isolateId; | |
| 370 final int _workerId; | |
| 371 } | 438 } |
| 372 | 439 |
| 373 /** Default factory for receive ports. */ | 440 /** Default factory for receive ports. */ |
| 374 class ReceivePortFactory { | 441 class ReceivePortFactory { |
| 375 | 442 |
| 376 factory ReceivePort() { | 443 factory ReceivePort() { |
| 377 return new ReceivePortImpl(); | 444 return new ReceivePortImpl(); |
| 378 } | 445 } |
| 379 | 446 |
| 380 factory ReceivePort.singleShot() { | 447 factory ReceivePort.singleShot() { |
| 381 return new ReceivePortSingleShotImpl(); | 448 return new ReceivePortSingleShotImpl(); |
| 382 } | 449 } |
| 383 } | 450 } |
| 384 | 451 |
| 385 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ | 452 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ |
| 386 class ReceivePortImpl implements ReceivePort { | 453 class ReceivePortImpl implements ReceivePort { |
| 454 int _id; | |
| 455 Function _callback; | |
| 456 static int _nextFreeId = 1; | |
| 457 | |
| 387 ReceivePortImpl() | 458 ReceivePortImpl() |
| 388 : _id = _nextFreeId++ { | 459 : _id = _nextFreeId++ { |
| 389 _globalState.currentContext.register(_id, this); | 460 _globalState.currentContext.register(_id, this); |
| 390 } | 461 } |
| 391 | 462 |
| 392 void receive(void onMessage(var message, SendPort replyTo)) { | 463 void receive(void onMessage(var message, SendPort replyTo)) { |
| 393 _callback = onMessage; | 464 _callback = onMessage; |
| 394 } | 465 } |
| 395 | 466 |
| 396 void close() { | 467 void close() { |
| 397 _callback = null; | 468 _callback = null; |
| 398 _globalState.currentContext.unregister(_id); | 469 _globalState.currentContext.unregister(_id); |
| 399 } | 470 } |
| 400 | 471 |
| 401 /** | |
| 402 * Returns a fresh [SendPort]. The implementation is not allowed to cache | |
| 403 * existing ports. | |
| 404 */ | |
| 405 SendPort toSendPort() { | 472 SendPort toSendPort() { |
| 406 return new SendPortImpl( | 473 return new NativeJsSendPort(this, _globalState.currentContext.id); |
| 407 _globalState.currentWorkerId, _globalState.currentContext.id, _id); | |
| 408 } | 474 } |
| 409 | |
| 410 int _id; | |
| 411 Function _callback; | |
| 412 | |
| 413 static int _nextFreeId = 1; | |
| 414 } | 475 } |
| 415 | 476 |
| 416 /** Implementation of a single-shot [ReceivePort]. */ | 477 /** Implementation of a single-shot [ReceivePort]. */ |
| 417 class ReceivePortSingleShotImpl implements ReceivePort { | 478 class ReceivePortSingleShotImpl implements ReceivePort { |
| 418 | 479 |
| 419 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } | 480 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } |
| 420 | 481 |
| 421 void receive(void callback(var message, SendPort replyTo)) { | 482 void receive(void callback(var message, SendPort replyTo)) { |
| 422 _port.receive((var message, SendPort replyTo) { | 483 _port.receive((var message, SendPort replyTo) { |
| 423 _port.close(); | 484 _port.close(); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 544 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { | 605 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { |
| 545 var replyTo = _deserializeMessage(serializedReplyTo); | 606 var replyTo = _deserializeMessage(serializedReplyTo); |
| 546 IsolateNatives._startIsolate(runnerObject, replyTo); | 607 IsolateNatives._startIsolate(runnerObject, replyTo); |
| 547 }, 'worker-start'); | 608 }, 'worker-start'); |
| 548 _globalState.topEventLoop.run(); | 609 _globalState.topEventLoop.run(); |
| 549 break; | 610 break; |
| 550 case 'spawn-worker': | 611 case 'spawn-worker': |
| 551 _spawnWorker(msg['factoryName'], msg['replyPort']); | 612 _spawnWorker(msg['factoryName'], msg['replyPort']); |
| 552 break; | 613 break; |
| 553 case 'message': | 614 case 'message': |
| 554 _sendMessage(msg['workerId'], msg['isolateId'], msg['portId'], | 615 msg['port'].send(msg['msg'], msg['replyTo']); |
| 555 msg['msg'], msg['replyTo']); | |
| 556 _globalState.topEventLoop.run(); | 616 _globalState.topEventLoop.run(); |
| 557 break; | 617 break; |
| 558 case 'close': | 618 case 'close': |
| 559 _log("Closing Worker"); | 619 _log("Closing Worker"); |
| 560 _globalState.workers.remove(sender.id); | 620 _globalState.workers.remove(sender.id); |
| 561 sender.terminate(); | 621 sender.terminate(); |
| 562 _globalState.topEventLoop.run(); | 622 _globalState.topEventLoop.run(); |
| 563 break; | 623 break; |
| 564 case 'log': | 624 case 'log': |
| 565 _log(msg['msg']); | 625 _log(msg['msg']); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 631 }, 'nonworker start'); | 691 }, 'nonworker start'); |
| 632 } | 692 } |
| 633 | 693 |
| 634 /** Given a ready-to-start runnable, start running it. */ | 694 /** Given a ready-to-start runnable, start running it. */ |
| 635 static void _startIsolate(Isolate isolate, SendPort replyTo) { | 695 static void _startIsolate(Isolate isolate, SendPort replyTo) { |
| 636 _fillStatics(_globalState.currentContext); | 696 _fillStatics(_globalState.currentContext); |
| 637 ReceivePort port = new ReceivePort(); | 697 ReceivePort port = new ReceivePort(); |
| 638 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 698 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
| 639 isolate._run(port); | 699 isolate._run(port); |
| 640 } | 700 } |
| 641 | |
| 642 static void _sendMessage(int workerId, int isolateId, int receivePortId, | |
| 643 message, replyTo) { | |
| 644 // Both the message and the replyTo are already serialized. | |
| 645 if (workerId == _globalState.currentWorkerId) { | |
| 646 var isolate = _globalState.isolates[isolateId]; | |
| 647 if (isolate == null) return; // Isolate has been closed. | |
| 648 var receivePort = isolate.lookup(receivePortId); | |
| 649 if (receivePort == null) return; // ReceivePort has been closed. | |
| 650 _globalState.topEventLoop.enqueue(isolate, () { | |
| 651 if (receivePort._callback != null) { | |
| 652 receivePort._callback( | |
| 653 _deserializeMessage(message), _deserializeMessage(replyTo)); | |
| 654 } | |
| 655 }, 'receive ' + message); | |
| 656 } else { | |
| 657 var worker; | |
| 658 // communication between workers go through the main worker | |
| 659 if (_globalState.isWorker) { | |
| 660 worker = _globalState.mainWorker; | |
| 661 } else { | |
| 662 // TODO(sigmund): make sure this works | |
| 663 worker = _globalState.workers[workerId]; | |
| 664 } | |
| 665 worker.postMessage(_serializeMessage({ | |
| 666 'command': 'message', | |
| 667 'workerId': workerId, | |
| 668 'isolateId': isolateId, | |
| 669 'portId': receivePortId, | |
| 670 'msg': message, | |
| 671 'replyTo': replyTo })); | |
| 672 } | |
| 673 } | |
| 674 } | 701 } |
| OLD | NEW |