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 /** Id for the destination isolate. */ | |
| 332 final int _isolateId; | |
| 331 | 333 |
| 332 const SendPortImpl(this._workerId, this._isolateId, this._receivePortId); | 334 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 | 335 |
| 345 ReceivePortSingleShotImpl call(var message) { | 336 ReceivePortSingleShotImpl call(var message) { |
| 346 final result = new ReceivePortSingleShotImpl(); | 337 final result = new ReceivePortSingleShotImpl(); |
| 347 this.send(message, result.toSendPort()); | 338 this.send(message, result.toSendPort()); |
| 348 return result; | 339 return result; |
| 349 } | 340 } |
| 350 | 341 |
| 351 ReceivePortSingleShotImpl _callNow(var message) { | 342 static void checkReplyTo(SendPort replyTo) { |
| 352 final result = new ReceivePortSingleShotImpl(); | 343 if (replyTo !== null && replyTo is! NativeJsSendPort |
| 353 send(message, result.toSendPort()); | 344 && replyTo is! WorkerSendPort) { |
| 354 return result; | 345 throw "SendPort.send: Illegal replyTo port type."; |
| 346 } | |
| 355 } | 347 } |
| 356 | 348 |
| 357 bool operator==(var other) { | 349 // TODO(sigmund): replace the current SendPort.call with the following: |
| 358 return (other is SendPortImpl) && | 350 //Future call(var message) { |
| 351 // final completer = new Completer(); | |
| 352 // final port = new ReceivePort.singleShot(); | |
| 353 // send(message, port.toSendPort()); | |
| 354 // port.receive((value, ignoreReplyTo) { | |
| 355 // if (value is Exception) { | |
| 356 // completer.completeException(value); | |
| 357 // } else { | |
| 358 // completer.complete(value); | |
| 359 // } | |
| 360 // }); | |
| 361 // return completer.future; | |
| 362 //} | |
| 363 | |
| 364 abstract void send(var message, [SendPort replyTo]); | |
| 365 abstract bool operator ==(var other); | |
| 366 abstract int hashCode(); | |
| 367 } | |
| 368 | |
| 369 /** A send port that delivers messages in-memory via native JavaScript calls. */ | |
| 370 class NativeJsSendPort extends BaseSendPort implements SendPort { | |
| 371 final ReceivePortImpl _receivePort; | |
| 372 | |
| 373 const NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId); | |
| 374 | |
| 375 void send(var message, [SendPort replyTo = null]) { | |
| 376 checkReplyTo(replyTo); | |
| 377 // Check that the isolate still runs and the port is still open | |
| 378 final isolate = _globalState.isolates[_isolateId]; | |
| 379 if (isolate == null) return; | |
| 380 if (_receivePort._callback == null) return; | |
| 381 | |
| 382 // We force serialization/deserialization as a simple way to ensure isolate | |
| 383 // communication restrictions are respected between isolates that live in | |
| 384 // the same worker. NativeJsSendPort delivers both messages from the same | |
| 385 // worker and messages from other workers. In particular, messages sent from | |
| 386 // a worker via a WorkerSendPort are received at [_processWorkerMessage] and | |
| 387 // forwarded to a native port. In such cases, here we'll see | |
| 388 // [_globalState.currentContext == null]. | |
| 389 final shouldSerialize = _globalState.currentContext != null | |
| 390 && _globalState.currentContext.id != _isolateId; | |
| 391 if (shouldSerialize) { | |
| 392 message = _serializeMessage(message); | |
| 393 replyTo = _serializeMessage(replyTo); | |
| 394 } | |
| 395 _globalState.topEventLoop.enqueue(isolate, () { | |
| 396 if (_receivePort._callback != null) { | |
| 397 if (shouldSerialize) { | |
|
eub
2012/02/10 21:20:57
I liked your previous version with the serialize-d
Siggi Cherem (dart-lang)
2012/02/10 22:09:22
Me too - when I did some testing, I realized that
eub
2012/02/10 22:11:50
Can we come up with a simple comment explaining to
| |
| 398 message = _deserializeMessage(message); | |
| 399 replyTo = _deserializeMessage(replyTo); | |
| 400 } | |
| 401 _receivePort._callback(message, replyTo); | |
| 402 } | |
| 403 }, 'receive ' + message); | |
| 404 } | |
| 405 | |
| 406 bool operator ==(var other) => (other is NativeJsSendPort) && | |
| 407 (_receivePort == other._receivePort); | |
| 408 | |
| 409 int hashCode() => _receivePort._id; | |
| 410 } | |
| 411 | |
| 412 /** A send port that delivers messages via worker.postMessage. */ | |
| 413 class WorkerSendPort extends BaseSendPort implements SendPort { | |
| 414 final int _workerId; | |
| 415 final int _receivePortId; | |
| 416 | |
| 417 const WorkerSendPort(this._workerId, int isolateId, this._receivePortId) | |
| 418 : super(isolateId); | |
| 419 | |
| 420 void send(var message, [SendPort replyTo = null]) { | |
| 421 checkReplyTo(replyTo); | |
| 422 final workerMessage = _serializeMessage({ | |
| 423 'command': 'message', | |
| 424 'port': this, | |
| 425 'msg': message, | |
| 426 'replyTo': replyTo}); | |
| 427 | |
| 428 if (_globalState.isWorker) { | |
| 429 // communication from one worker to another go through the main worker: | |
| 430 _globalState.mainWorker.postMessage(workerMessage); | |
| 431 } else { | |
| 432 _globalState.workers[_workerId].postMessage(workerMessage); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 bool operator ==(var other) { | |
| 437 return (other is WorkerSendPort) && | |
| 359 (_workerId == other._workerId) && | 438 (_workerId == other._workerId) && |
| 360 (_isolateId == other._isolateId) && | 439 (_isolateId == other._isolateId) && |
| 361 (_receivePortId == other._receivePortId); | 440 (_receivePortId == other._receivePortId); |
| 362 } | 441 } |
| 363 | 442 |
| 364 int hashCode() { | 443 int hashCode() { |
| 444 // TODO(sigmund): use a standard hash when we get one available in corelib. | |
| 365 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; | 445 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; |
| 366 } | 446 } |
| 367 | |
| 368 final int _receivePortId; | |
| 369 final int _isolateId; | |
| 370 final int _workerId; | |
| 371 } | 447 } |
| 372 | 448 |
| 373 /** Default factory for receive ports. */ | 449 /** Default factory for receive ports. */ |
| 374 class ReceivePortFactory { | 450 class ReceivePortFactory { |
| 375 | 451 |
| 376 factory ReceivePort() { | 452 factory ReceivePort() { |
| 377 return new ReceivePortImpl(); | 453 return new ReceivePortImpl(); |
| 378 } | 454 } |
| 379 | 455 |
| 380 factory ReceivePort.singleShot() { | 456 factory ReceivePort.singleShot() { |
| 381 return new ReceivePortSingleShotImpl(); | 457 return new ReceivePortSingleShotImpl(); |
| 382 } | 458 } |
| 383 } | 459 } |
| 384 | 460 |
| 385 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ | 461 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ |
| 386 class ReceivePortImpl implements ReceivePort { | 462 class ReceivePortImpl implements ReceivePort { |
| 463 int _id; | |
| 464 Function _callback; | |
| 465 static int _nextFreeId = 1; | |
| 466 | |
| 387 ReceivePortImpl() | 467 ReceivePortImpl() |
| 388 : _id = _nextFreeId++ { | 468 : _id = _nextFreeId++ { |
| 389 _globalState.currentContext.register(_id, this); | 469 _globalState.currentContext.register(_id, this); |
| 390 } | 470 } |
| 391 | 471 |
| 392 void receive(void onMessage(var message, SendPort replyTo)) { | 472 void receive(void onMessage(var message, SendPort replyTo)) { |
| 393 _callback = onMessage; | 473 _callback = onMessage; |
| 394 } | 474 } |
| 395 | 475 |
| 396 void close() { | 476 void close() { |
| 397 _callback = null; | 477 _callback = null; |
| 398 _globalState.currentContext.unregister(_id); | 478 _globalState.currentContext.unregister(_id); |
| 399 } | 479 } |
| 400 | 480 |
| 401 /** | |
| 402 * Returns a fresh [SendPort]. The implementation is not allowed to cache | |
| 403 * existing ports. | |
| 404 */ | |
| 405 SendPort toSendPort() { | 481 SendPort toSendPort() { |
| 406 return new SendPortImpl( | 482 return new NativeJsSendPort(this, _globalState.currentContext.id); |
| 407 _globalState.currentWorkerId, _globalState.currentContext.id, _id); | |
| 408 } | 483 } |
| 409 | |
| 410 int _id; | |
| 411 Function _callback; | |
| 412 | |
| 413 static int _nextFreeId = 1; | |
| 414 } | 484 } |
| 415 | 485 |
| 416 /** Implementation of a single-shot [ReceivePort]. */ | 486 /** Implementation of a single-shot [ReceivePort]. */ |
| 417 class ReceivePortSingleShotImpl implements ReceivePort { | 487 class ReceivePortSingleShotImpl implements ReceivePort { |
| 418 | 488 |
| 419 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } | 489 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } |
| 420 | 490 |
| 421 void receive(void callback(var message, SendPort replyTo)) { | 491 void receive(void callback(var message, SendPort replyTo)) { |
| 422 _port.receive((var message, SendPort replyTo) { | 492 _port.receive((var message, SendPort replyTo) { |
| 423 _port.close(); | 493 _port.close(); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 544 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { | 614 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { |
| 545 var replyTo = _deserializeMessage(serializedReplyTo); | 615 var replyTo = _deserializeMessage(serializedReplyTo); |
| 546 IsolateNatives._startIsolate(runnerObject, replyTo); | 616 IsolateNatives._startIsolate(runnerObject, replyTo); |
| 547 }, 'worker-start'); | 617 }, 'worker-start'); |
| 548 _globalState.topEventLoop.run(); | 618 _globalState.topEventLoop.run(); |
| 549 break; | 619 break; |
| 550 case 'spawn-worker': | 620 case 'spawn-worker': |
| 551 _spawnWorker(msg['factoryName'], msg['replyPort']); | 621 _spawnWorker(msg['factoryName'], msg['replyPort']); |
| 552 break; | 622 break; |
| 553 case 'message': | 623 case 'message': |
| 554 _sendMessage(msg['workerId'], msg['isolateId'], msg['portId'], | 624 msg['port'].send(msg['msg'], msg['replyTo']); |
| 555 msg['msg'], msg['replyTo']); | |
| 556 _globalState.topEventLoop.run(); | 625 _globalState.topEventLoop.run(); |
| 557 break; | 626 break; |
| 558 case 'close': | 627 case 'close': |
| 559 _log("Closing Worker"); | 628 _log("Closing Worker"); |
| 560 _globalState.workers.remove(sender.id); | 629 _globalState.workers.remove(sender.id); |
| 561 sender.terminate(); | 630 sender.terminate(); |
| 562 _globalState.topEventLoop.run(); | 631 _globalState.topEventLoop.run(); |
| 563 break; | 632 break; |
| 564 case 'log': | 633 case 'log': |
| 565 _log(msg['msg']); | 634 _log(msg['msg']); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 631 }, 'nonworker start'); | 700 }, 'nonworker start'); |
| 632 } | 701 } |
| 633 | 702 |
| 634 /** Given a ready-to-start runnable, start running it. */ | 703 /** Given a ready-to-start runnable, start running it. */ |
| 635 static void _startIsolate(Isolate isolate, SendPort replyTo) { | 704 static void _startIsolate(Isolate isolate, SendPort replyTo) { |
| 636 _fillStatics(_globalState.currentContext); | 705 _fillStatics(_globalState.currentContext); |
| 637 ReceivePort port = new ReceivePort(); | 706 ReceivePort port = new ReceivePort(); |
| 638 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 707 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
| 639 isolate._run(port); | 708 isolate._run(port); |
| 640 } | 709 } |
| 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 } | 710 } |
| OLD | NEW |