| 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 var msg = message; |
| 392 var reply = replyTo; |
| 393 if (shouldSerialize) { |
| 394 msg = _serializeMessage(msg); |
| 395 reply = _serializeMessage(reply); |
| 396 } |
| 397 _globalState.topEventLoop.enqueue(isolate, () { |
| 398 if (_receivePort._callback != null) { |
| 399 if (shouldSerialize) { |
| 400 msg = _deserializeMessage(msg); |
| 401 reply = _deserializeMessage(reply); |
| 402 } |
| 403 _receivePort._callback(msg, reply); |
| 404 } |
| 405 }, 'receive ' + message); |
| 406 } |
| 407 |
| 408 bool operator ==(var other) => (other is NativeJsSendPort) && |
| 409 (_receivePort == other._receivePort); |
| 410 |
| 411 int hashCode() => _receivePort._id; |
| 412 } |
| 413 |
| 414 /** A send port that delivers messages via worker.postMessage. */ |
| 415 class WorkerSendPort extends BaseSendPort implements SendPort { |
| 416 final int _workerId; |
| 417 final int _receivePortId; |
| 418 |
| 419 const WorkerSendPort(this._workerId, int isolateId, this._receivePortId) |
| 420 : super(isolateId); |
| 421 |
| 422 void send(var message, [SendPort replyTo = null]) { |
| 423 checkReplyTo(replyTo); |
| 424 final workerMessage = _serializeMessage({ |
| 425 'command': 'message', |
| 426 'port': this, |
| 427 'msg': message, |
| 428 'replyTo': replyTo}); |
| 429 |
| 430 if (_globalState.isWorker) { |
| 431 // communication from one worker to another go through the main worker: |
| 432 _globalState.mainWorker.postMessage(workerMessage); |
| 433 } else { |
| 434 _globalState.workers[_workerId].postMessage(workerMessage); |
| 435 } |
| 436 } |
| 437 |
| 438 bool operator ==(var other) { |
| 439 return (other is WorkerSendPort) && |
| 359 (_workerId == other._workerId) && | 440 (_workerId == other._workerId) && |
| 360 (_isolateId == other._isolateId) && | 441 (_isolateId == other._isolateId) && |
| 361 (_receivePortId == other._receivePortId); | 442 (_receivePortId == other._receivePortId); |
| 362 } | 443 } |
| 363 | 444 |
| 364 int hashCode() { | 445 int hashCode() { |
| 446 // TODO(sigmund): use a standard hash when we get one available in corelib. |
| 365 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; | 447 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; |
| 366 } | 448 } |
| 367 | |
| 368 final int _receivePortId; | |
| 369 final int _isolateId; | |
| 370 final int _workerId; | |
| 371 } | 449 } |
| 372 | 450 |
| 373 /** Default factory for receive ports. */ | 451 /** Default factory for receive ports. */ |
| 374 class ReceivePortFactory { | 452 class ReceivePortFactory { |
| 375 | 453 |
| 376 factory ReceivePort() { | 454 factory ReceivePort() { |
| 377 return new ReceivePortImpl(); | 455 return new ReceivePortImpl(); |
| 378 } | 456 } |
| 379 | 457 |
| 380 factory ReceivePort.singleShot() { | 458 factory ReceivePort.singleShot() { |
| 381 return new ReceivePortSingleShotImpl(); | 459 return new ReceivePortSingleShotImpl(); |
| 382 } | 460 } |
| 383 } | 461 } |
| 384 | 462 |
| 385 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ | 463 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */ |
| 386 class ReceivePortImpl implements ReceivePort { | 464 class ReceivePortImpl implements ReceivePort { |
| 465 int _id; |
| 466 Function _callback; |
| 467 static int _nextFreeId = 1; |
| 468 |
| 387 ReceivePortImpl() | 469 ReceivePortImpl() |
| 388 : _id = _nextFreeId++ { | 470 : _id = _nextFreeId++ { |
| 389 _globalState.currentContext.register(_id, this); | 471 _globalState.currentContext.register(_id, this); |
| 390 } | 472 } |
| 391 | 473 |
| 392 void receive(void onMessage(var message, SendPort replyTo)) { | 474 void receive(void onMessage(var message, SendPort replyTo)) { |
| 393 _callback = onMessage; | 475 _callback = onMessage; |
| 394 } | 476 } |
| 395 | 477 |
| 396 void close() { | 478 void close() { |
| 397 _callback = null; | 479 _callback = null; |
| 398 _globalState.currentContext.unregister(_id); | 480 _globalState.currentContext.unregister(_id); |
| 399 } | 481 } |
| 400 | 482 |
| 401 /** | |
| 402 * Returns a fresh [SendPort]. The implementation is not allowed to cache | |
| 403 * existing ports. | |
| 404 */ | |
| 405 SendPort toSendPort() { | 483 SendPort toSendPort() { |
| 406 return new SendPortImpl( | 484 return new NativeJsSendPort(this, _globalState.currentContext.id); |
| 407 _globalState.currentWorkerId, _globalState.currentContext.id, _id); | |
| 408 } | 485 } |
| 409 | |
| 410 int _id; | |
| 411 Function _callback; | |
| 412 | |
| 413 static int _nextFreeId = 1; | |
| 414 } | 486 } |
| 415 | 487 |
| 416 /** Implementation of a single-shot [ReceivePort]. */ | 488 /** Implementation of a single-shot [ReceivePort]. */ |
| 417 class ReceivePortSingleShotImpl implements ReceivePort { | 489 class ReceivePortSingleShotImpl implements ReceivePort { |
| 418 | 490 |
| 419 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } | 491 ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } |
| 420 | 492 |
| 421 void receive(void callback(var message, SendPort replyTo)) { | 493 void receive(void callback(var message, SendPort replyTo)) { |
| 422 _port.receive((var message, SendPort replyTo) { | 494 _port.receive((var message, SendPort replyTo) { |
| 423 _port.close(); | 495 _port.close(); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 544 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { | 616 _globalState.topEventLoop.enqueue(new IsolateContext(), function() { |
| 545 var replyTo = _deserializeMessage(serializedReplyTo); | 617 var replyTo = _deserializeMessage(serializedReplyTo); |
| 546 IsolateNatives._startIsolate(runnerObject, replyTo); | 618 IsolateNatives._startIsolate(runnerObject, replyTo); |
| 547 }, 'worker-start'); | 619 }, 'worker-start'); |
| 548 _globalState.topEventLoop.run(); | 620 _globalState.topEventLoop.run(); |
| 549 break; | 621 break; |
| 550 case 'spawn-worker': | 622 case 'spawn-worker': |
| 551 _spawnWorker(msg['factoryName'], msg['replyPort']); | 623 _spawnWorker(msg['factoryName'], msg['replyPort']); |
| 552 break; | 624 break; |
| 553 case 'message': | 625 case 'message': |
| 554 _sendMessage(msg['workerId'], msg['isolateId'], msg['portId'], | 626 msg['port'].send(msg['msg'], msg['replyTo']); |
| 555 msg['msg'], msg['replyTo']); | |
| 556 _globalState.topEventLoop.run(); | 627 _globalState.topEventLoop.run(); |
| 557 break; | 628 break; |
| 558 case 'close': | 629 case 'close': |
| 559 _log("Closing Worker"); | 630 _log("Closing Worker"); |
| 560 _globalState.workers.remove(sender.id); | 631 _globalState.workers.remove(sender.id); |
| 561 sender.terminate(); | 632 sender.terminate(); |
| 562 _globalState.topEventLoop.run(); | 633 _globalState.topEventLoop.run(); |
| 563 break; | 634 break; |
| 564 case 'log': | 635 case 'log': |
| 565 _log(msg['msg']); | 636 _log(msg['msg']); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 631 }, 'nonworker start'); | 702 }, 'nonworker start'); |
| 632 } | 703 } |
| 633 | 704 |
| 634 /** Given a ready-to-start runnable, start running it. */ | 705 /** Given a ready-to-start runnable, start running it. */ |
| 635 static void _startIsolate(Isolate isolate, SendPort replyTo) { | 706 static void _startIsolate(Isolate isolate, SendPort replyTo) { |
| 636 _fillStatics(_globalState.currentContext); | 707 _fillStatics(_globalState.currentContext); |
| 637 ReceivePort port = new ReceivePort(); | 708 ReceivePort port = new ReceivePort(); |
| 638 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 709 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
| 639 isolate._run(port); | 710 isolate._run(port); |
| 640 } | 711 } |
| 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 } | 712 } |
| OLD | NEW |