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 /** | 5 /** |
6 * Concepts used here: | 6 * Concepts used here: |
7 * | 7 * |
8 * "manager" - A manager contains one or more isolates, schedules their | 8 * "manager" - A manager contains one or more isolates, schedules their |
9 * execution, and performs other plumbing on their behalf. The isolate | 9 * execution, and performs other plumbing on their behalf. The isolate |
10 * present at the creation of the manager is designated as its "root isolate". | 10 * present at the creation of the manager is designated as its "root isolate". |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 } | 45 } |
46 return _lazyPort; | 46 return _lazyPort; |
47 } | 47 } |
48 | 48 |
49 SendPort _spawnFunction(void topLevelFunction()) { | 49 SendPort _spawnFunction(void topLevelFunction()) { |
50 final name = _IsolateNatives._getJSFunctionName(topLevelFunction); | 50 final name = _IsolateNatives._getJSFunctionName(topLevelFunction); |
51 if (name == null) { | 51 if (name == null) { |
52 throw new UnsupportedOperationException( | 52 throw new UnsupportedOperationException( |
53 "only top-level functions can be spawned."); | 53 "only top-level functions can be spawned."); |
54 } | 54 } |
55 return _IsolateNatives._spawn2(name, null, false); | 55 return _IsolateNatives._spawn(name, null, false); |
56 } | 56 } |
57 | 57 |
58 SendPort _spawnUri(String uri) { | 58 SendPort _spawnUri(String uri) { |
59 return _IsolateNatives._spawn2(null, uri, false); | 59 return _IsolateNatives._spawn(null, uri, false); |
60 } | 60 } |
61 | 61 |
62 /** State associated with the current manager. See [globalState]. */ | 62 /** State associated with the current manager. See [globalState]. */ |
63 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? | 63 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? |
64 class _Manager { | 64 class _Manager { |
65 | 65 |
66 /** Next available isolate id within this [_Manager]. */ | 66 /** Next available isolate id within this [_Manager]. */ |
67 int nextIsolateId = 0; | 67 int nextIsolateId = 0; |
68 | 68 |
69 /** id assigned to this [_Manager]. */ | 69 /** id assigned to this [_Manager]. */ |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 void set onmessage(f) native "this.onmessage = f;"; | 352 void set onmessage(f) native "this.onmessage = f;"; |
353 void postMessage(msg) native "return this.postMessage(msg);"; | 353 void postMessage(msg) native "return this.postMessage(msg);"; |
354 // terminate() is implemented by Worker. | 354 // terminate() is implemented by Worker. |
355 abstract void terminate(); | 355 abstract void terminate(); |
356 } | 356 } |
357 | 357 |
358 final String _SPAWNED_SIGNAL = "spawned"; | 358 final String _SPAWNED_SIGNAL = "spawned"; |
359 | 359 |
360 class _IsolateNatives { | 360 class _IsolateNatives { |
361 | 361 |
362 /** JavaScript-specific implementation to spawn an isolate. */ | |
363 static Future<SendPort> spawn(Isolate isolate, bool isLight) { | |
364 Completer<SendPort> completer = new Completer<SendPort>(); | |
365 ReceivePort port = new ReceivePort(); | |
366 port.receive((msg, SendPort replyPort) { | |
367 port.close(); | |
368 assert(msg == _SPAWNED_SIGNAL); | |
369 completer.complete(replyPort); | |
370 }); | |
371 | |
372 // TODO(floitsch): throw exception if isolate's class doesn't have a | |
373 // default constructor. | |
374 if (_globalState.useWorkers && !isLight) { | |
375 _startWorker(isolate, port.toSendPort()); | |
376 } else { | |
377 _startNonWorker(isolate, port.toSendPort()); | |
378 } | |
379 | |
380 return completer.future; | |
381 } | |
382 | |
383 static SendPort _startWorker(Isolate runnable, SendPort replyPort) { | |
384 var factoryName = _getJSConstructorName(runnable); | |
385 if (_globalState.isWorker) { | |
386 _globalState.mainManager.postMessage(_serializeMessage({ | |
387 'command': 'spawn-worker', | |
388 'factoryName': factoryName, | |
389 'replyPort': _serializeMessage(replyPort)})); | |
390 } else { | |
391 _spawnWorker(factoryName, _serializeMessage(replyPort)); | |
392 } | |
393 } | |
394 | |
395 /** | 362 /** |
396 * The src url for the script tag that loaded this code. Used to create | 363 * The src url for the script tag that loaded this code. Used to create |
397 * JavaScript workers. | 364 * JavaScript workers. |
398 */ | 365 */ |
399 static String get _thisScript() native @"return $thisScriptUrl"; | 366 static String get _thisScript() native @"return $thisScriptUrl"; |
400 | 367 |
401 /** Starts a new worker with the given URL. */ | 368 /** Starts a new worker with the given URL. */ |
402 static _WorkerStub _newWorker(url) native "return new Worker(url);"; | 369 static _WorkerStub _newWorker(url) native "return new Worker(url);"; |
403 | 370 |
404 /** | 371 /** |
405 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor | |
406 * name for the isolate entry point class. | |
407 */ | |
408 static void _spawnWorker(factoryName, serializedReplyPort) { | |
409 final worker = _newWorker(_thisScript); | |
410 worker.onmessage = (e) { _processWorkerMessage(worker, e); }; | |
411 var workerId = _globalState.nextManagerId++; | |
412 // We also store the id on the worker itself so that we can unregister it. | |
413 worker.id = workerId; | |
414 _globalState.managers[workerId] = worker; | |
415 worker.postMessage(_serializeMessage({ | |
416 'command': 'start', | |
417 'id': workerId, | |
418 'replyTo': serializedReplyPort, | |
419 'factoryName': factoryName })); | |
420 } | |
421 | |
422 /** | |
423 * Assume that [e] is a browser message event and extract its message data. | 372 * Assume that [e] is a browser message event and extract its message data. |
424 * We don't import the dom explicitly so, when workers are disabled, this | 373 * We don't import the dom explicitly so, when workers are disabled, this |
425 * library can also run on top of nodejs. | 374 * library can also run on top of nodejs. |
426 */ | 375 */ |
427 static _getEventData(e) native "return e.data"; | 376 static _getEventData(e) native "return e.data"; |
428 | 377 |
429 /** | 378 /** |
430 * Process messages on a worker, either to control the worker instance or to | 379 * Process messages on a worker, either to control the worker instance or to |
431 * pass messages along to the isolate running in the worker. | 380 * pass messages along to the isolate running in the worker. |
432 */ | 381 */ |
433 static void _processWorkerMessage(sender, e) { | 382 static void _processWorkerMessage(sender, e) { |
434 var msg = _deserializeMessage(_getEventData(e)); | 383 var msg = _deserializeMessage(_getEventData(e)); |
435 switch (msg['command']) { | 384 switch (msg['command']) { |
436 // TODO(sigmund): delete after we migrate to the new API | |
437 case 'start': | 385 case 'start': |
438 _globalState.currentManagerId = msg['id']; | 386 _globalState.currentManagerId = msg['id']; |
439 var runnerObject = | |
440 _allocate(_getJSConstructorFromName(msg['factoryName'])); | |
441 var serializedReplyTo = msg['replyTo']; | |
442 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { | |
443 var replyTo = _deserializeMessage(serializedReplyTo); | |
444 _startIsolate(runnerObject, replyTo); | |
445 }, 'worker-start'); | |
446 _globalState.topEventLoop.run(); | |
447 break; | |
448 case 'start2': | |
449 _globalState.currentManagerId = msg['id']; | |
450 Function entryPoint = _getJSFunctionFromName(msg['functionName']); | 387 Function entryPoint = _getJSFunctionFromName(msg['functionName']); |
451 var replyTo = _deserializeMessage(msg['replyTo']); | 388 var replyTo = _deserializeMessage(msg['replyTo']); |
452 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { | 389 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { |
453 _startIsolate2(entryPoint, replyTo); | 390 _startIsolate(entryPoint, replyTo); |
454 }, 'worker-start'); | 391 }, 'worker-start'); |
455 _globalState.topEventLoop.run(); | 392 _globalState.topEventLoop.run(); |
456 break; | 393 break; |
457 // TODO(sigmund): delete after we migrate to the new API | |
458 case 'spawn-worker': | 394 case 'spawn-worker': |
459 _spawnWorker(msg['factoryName'], msg['replyPort']); | 395 _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']); |
460 break; | |
461 case 'spawn-worker2': | |
462 _spawnWorker2(msg['functionName'], msg['uri'], msg['replyPort']); | |
463 break; | 396 break; |
464 case 'message': | 397 case 'message': |
465 msg['port'].send(msg['msg'], msg['replyTo']); | 398 msg['port'].send(msg['msg'], msg['replyTo']); |
466 _globalState.topEventLoop.run(); | 399 _globalState.topEventLoop.run(); |
467 break; | 400 break; |
468 case 'close': | 401 case 'close': |
469 _log("Closing Worker"); | 402 _log("Closing Worker"); |
470 _globalState.managers.remove(sender.id); | 403 _globalState.managers.remove(sender.id); |
471 sender.terminate(); | 404 sender.terminate(); |
472 _globalState.topEventLoop.run(); | 405 _globalState.topEventLoop.run(); |
(...skipping 23 matching lines...) Expand all Loading... |
496 try { | 429 try { |
497 _consoleLog(msg); | 430 _consoleLog(msg); |
498 } catch(var e, var trace) { | 431 } catch(var e, var trace) { |
499 throw new Exception(trace); | 432 throw new Exception(trace); |
500 } | 433 } |
501 } | 434 } |
502 } | 435 } |
503 | 436 |
504 static void _consoleLog(msg) native "\$globalThis.console.log(msg);"; | 437 static void _consoleLog(msg) native "\$globalThis.console.log(msg);"; |
505 | 438 |
506 | |
507 /** | 439 /** |
508 * Extract the constructor of runnable, so it can be allocated in another | 440 * Extract the constructor of runnable, so it can be allocated in another |
509 * isolate. | 441 * isolate. |
510 */ | 442 */ |
511 static Dynamic _getJSConstructor(Isolate runnable) native """ | 443 static Dynamic _getJSConstructor(Isolate runnable) native """ |
512 return runnable.constructor; | 444 return runnable.constructor; |
513 """; | 445 """; |
514 | 446 |
515 /** Extract the constructor name of a runnable */ | 447 /** Extract the constructor name of a runnable */ |
516 // TODO(sigmund): find a browser-generic way to support this. | 448 // TODO(sigmund): find a browser-generic way to support this. |
(...skipping 14 matching lines...) Expand all Loading... |
531 * Get a string name for the function, if possible. The result for | 463 * Get a string name for the function, if possible. The result for |
532 * anonymous functions is browser-dependent -- it may be "" or "anonymous" | 464 * anonymous functions is browser-dependent -- it may be "" or "anonymous" |
533 * but you should probably not count on this. | 465 * but you should probably not count on this. |
534 */ | 466 */ |
535 static String _getJSFunctionName(Function f) | 467 static String _getJSFunctionName(Function f) |
536 native @"return f.$name || (void 0);"; | 468 native @"return f.$name || (void 0);"; |
537 | 469 |
538 /** Create a new JavaScript object instance given its constructor. */ | 470 /** Create a new JavaScript object instance given its constructor. */ |
539 static Dynamic _allocate(var ctor) native "return new ctor();"; | 471 static Dynamic _allocate(var ctor) native "return new ctor();"; |
540 | 472 |
541 /** Starts a non-worker isolate. */ | |
542 static SendPort _startNonWorker(Isolate runnable, SendPort replyTo) { | |
543 // Spawn a new isolate and create the receive port in it. | |
544 final spawned = new _IsolateContext(); | |
545 | |
546 // Instead of just running the provided runnable, we create a | |
547 // new cloned instance of it with a fresh state in the spawned | |
548 // isolate. This way, we do not get cross-isolate references | |
549 // through the runnable. | |
550 final ctor = _getJSConstructor(runnable); | |
551 _globalState.topEventLoop.enqueue(spawned, function() { | |
552 _startIsolate(_allocate(ctor), replyTo); | |
553 }, 'nonworker start'); | |
554 } | |
555 | |
556 /** Given a ready-to-start runnable, start running it. */ | |
557 static void _startIsolate(Isolate isolate, SendPort replyTo) { | |
558 _fillStatics(_globalState.currentContext); | |
559 ReceivePort port = new ReceivePort(); | |
560 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | |
561 isolate._run(port); | |
562 } | |
563 | |
564 // TODO(sigmund): clean up above, after we make the new API the default: | 473 // TODO(sigmund): clean up above, after we make the new API the default: |
565 | 474 |
566 static _spawn2(String functionName, String uri, bool isLight) { | 475 static _spawn(String functionName, String uri, bool isLight) { |
567 Completer<SendPort> completer = new Completer<SendPort>(); | 476 Completer<SendPort> completer = new Completer<SendPort>(); |
568 ReceivePort port = new ReceivePort(); | 477 ReceivePort port = new ReceivePort(); |
569 port.receive((msg, SendPort replyPort) { | 478 port.receive((msg, SendPort replyPort) { |
570 port.close(); | 479 port.close(); |
571 assert(msg == _SPAWNED_SIGNAL); | 480 assert(msg == _SPAWNED_SIGNAL); |
572 completer.complete(replyPort); | 481 completer.complete(replyPort); |
573 }); | 482 }); |
574 | 483 |
575 SendPort signalReply = port.toSendPort(); | 484 SendPort signalReply = port.toSendPort(); |
576 | 485 |
577 if (_globalState.useWorkers && !isLight) { | 486 if (_globalState.useWorkers && !isLight) { |
578 _startWorker2(functionName, uri, signalReply); | 487 _startWorker(functionName, uri, signalReply); |
579 } else { | 488 } else { |
580 _startNonWorker2(functionName, uri, signalReply); | 489 _startNonWorker(functionName, uri, signalReply); |
581 } | 490 } |
582 return new _BufferingSendPort( | 491 return new _BufferingSendPort( |
583 _globalState.currentContext.id, completer.future); | 492 _globalState.currentContext.id, completer.future); |
584 } | 493 } |
585 | 494 |
586 static SendPort _startWorker2( | 495 static SendPort _startWorker( |
587 String functionName, String uri, SendPort replyPort) { | 496 String functionName, String uri, SendPort replyPort) { |
588 if (_globalState.isWorker) { | 497 if (_globalState.isWorker) { |
589 _globalState.mainManager.postMessage(_serializeMessage({ | 498 _globalState.mainManager.postMessage(_serializeMessage({ |
590 'command': 'spawn-worker2', | 499 'command': 'spawn-worker', |
591 'functionName': functionName, | 500 'functionName': functionName, |
592 'uri': uri, | 501 'uri': uri, |
593 'replyPort': replyPort})); | 502 'replyPort': replyPort})); |
594 } else { | 503 } else { |
595 _spawnWorker2(functionName, uri, replyPort); | 504 _spawnWorker(functionName, uri, replyPort); |
596 } | 505 } |
597 } | 506 } |
598 | 507 |
599 static SendPort _startNonWorker2( | 508 static SendPort _startNonWorker( |
600 String functionName, String uri, SendPort replyPort) { | 509 String functionName, String uri, SendPort replyPort) { |
601 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. | 510 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. |
602 if (uri != null) throw new UnsupportedOperationException( | 511 if (uri != null) throw new UnsupportedOperationException( |
603 "Currently spawnUri is not supported without web workers."); | 512 "Currently spawnUri is not supported without web workers."); |
604 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { | 513 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { |
605 final func = _getJSFunctionFromName(functionName); | 514 final func = _getJSFunctionFromName(functionName); |
606 _startIsolate2(func, replyPort); | 515 _startIsolate(func, replyPort); |
607 }, 'nonworker start'); | 516 }, 'nonworker start'); |
608 } | 517 } |
609 | 518 |
610 static void _startIsolate2(Function topLevel, SendPort replyTo) { | 519 static void _startIsolate(Function topLevel, SendPort replyTo) { |
611 _fillStatics(_globalState.currentContext); | 520 _fillStatics(_globalState.currentContext); |
612 _lazyPort = new ReceivePort(); | 521 _lazyPort = new ReceivePort(); |
613 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 522 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
614 topLevel(); | 523 topLevel(); |
615 } | 524 } |
616 | 525 |
617 /** | 526 /** |
618 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor | 527 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |
619 * name for the isolate entry point class. | 528 * name for the isolate entry point class. |
620 */ | 529 */ |
621 static void _spawnWorker2(functionName, uri, replyPort) { | 530 static void _spawnWorker(functionName, uri, replyPort) { |
622 if (functionName == null) functionName = 'main'; | 531 if (functionName == null) functionName = 'main'; |
623 if (uri == null) uri = _thisScript; | 532 if (uri == null) uri = _thisScript; |
624 if (!(new Uri.fromString(uri).isAbsolute())) { | 533 if (!(new Uri.fromString(uri).isAbsolute())) { |
625 // The constructor of dom workers requires an absolute URL. If we use a | 534 // The constructor of dom workers requires an absolute URL. If we use a |
626 // relative path we will get a DOM exception. | 535 // relative path we will get a DOM exception. |
627 String prefix = _thisScript.substring(0, _thisScript.lastIndexOf('/')); | 536 String prefix = _thisScript.substring(0, _thisScript.lastIndexOf('/')); |
628 uri = "$prefix/$uri"; | 537 uri = "$prefix/$uri"; |
629 } | 538 } |
630 final worker = _newWorker(uri); | 539 final worker = _newWorker(uri); |
631 worker.onmessage = (e) { _processWorkerMessage(worker, e); }; | 540 worker.onmessage = (e) { _processWorkerMessage(worker, e); }; |
632 var workerId = _globalState.nextManagerId++; | 541 var workerId = _globalState.nextManagerId++; |
633 // We also store the id on the worker itself so that we can unregister it. | 542 // We also store the id on the worker itself so that we can unregister it. |
634 worker.id = workerId; | 543 worker.id = workerId; |
635 _globalState.managers[workerId] = worker; | 544 _globalState.managers[workerId] = worker; |
636 worker.postMessage(_serializeMessage({ | 545 worker.postMessage(_serializeMessage({ |
637 'command': 'start2', | 546 'command': 'start', |
638 'id': workerId, | 547 'id': workerId, |
639 // Note: we serialize replyPort twice because the child worker needs to | 548 // Note: we serialize replyPort twice because the child worker needs to |
640 // first deserialize the worker id, before it can correctly deserialize | 549 // first deserialize the worker id, before it can correctly deserialize |
641 // the port (port deserialization is sensitive to what is the current | 550 // the port (port deserialization is sensitive to what is the current |
642 // workerId). | 551 // workerId). |
643 'replyTo': _serializeMessage(replyPort), | 552 'replyTo': _serializeMessage(replyPort), |
644 'functionName': functionName })); | 553 'functionName': functionName })); |
645 } | 554 } |
646 } | 555 } |
OLD | NEW |