| Index: sdk/lib/_internal/lib/isolate_helper.dart
|
| diff --git a/sdk/lib/_internal/lib/isolate_helper.dart b/sdk/lib/_internal/lib/isolate_helper.dart
|
| index 8a19dc68f8ec115055d8fa9849269fa69506318b..9245d07bae5e092ba92b947cac2b4a7c7e35e7bf 100644
|
| --- a/sdk/lib/_internal/lib/isolate_helper.dart
|
| +++ b/sdk/lib/_internal/lib/isolate_helper.dart
|
| @@ -280,6 +280,16 @@ class _IsolateContext implements IsolateContext {
|
| // Container with the "on exit" handler send-ports.
|
| var doneHandlers;
|
|
|
| + /**
|
| + * Queue of functions to call when the current event is complete.
|
| + *
|
| + * These events are not just put at the front of the event queue, because
|
| + * they represent control messages, and should be handled even if the
|
| + * event queue is paused.
|
| + */
|
| + var _scheduledControlEvents;
|
| + bool _isExecutingEvent = false;
|
| +
|
| /** Whether errors are considered fatal. */
|
| // This doesn't do anything yet. We need to be able to catch uncaught errors
|
| // (oxymoronically) in order to take lethal action. This is waiting for the
|
| @@ -332,15 +342,41 @@ class _IsolateContext implements IsolateContext {
|
| }
|
|
|
| void handlePing(SendPort responsePort, int pingType) {
|
| - if (pingType == Isolate.PING_EVENT) {
|
| - _globalState.topEventLoop.enqueue(this, () {
|
| - responsePort.send(null);
|
| - }, "ping");
|
| - } else {
|
| - // There is no difference between PING_ALIVE and PING_CONTROL
|
| - // since we don't handle it before the control event queue.
|
| + if (pingType == Isolate.IMMEDIATE ||
|
| + (pingType == Isolate.BEFORE_NEXT_EVENT &&
|
| + !_isExecutingEvent)) {
|
| responsePort.send(null);
|
| + return;
|
| + }
|
| + void respond() { responsePort.send(null); }
|
| + if (pingType == Isolate.AS_EVENT) {
|
| + _globalState.topEventLoop.enqueue(this, respond, "ping");
|
| + return;
|
| + }
|
| + assert(pingType == Isolate.BEFORE_NEXT_EVENT);
|
| + if (_scheduledControlEvents == null) {
|
| + _scheduledControlEvents = new Queue();
|
| + }
|
| + _scheduledControlEvents.addLast(respond);
|
| + }
|
| +
|
| + void handleKill(Capability authentification, int priority) {
|
| + if (this.terminateCapability != authentification) return;
|
| + if (priority == Isolate.IMMEDIATE ||
|
| + (priority == Isolate.BEFORE_NEXT_EVENT &&
|
| + !_isExecutingEvent)) {
|
| + kill();
|
| + return;
|
| + }
|
| + if (priority == Isolate.AS_EVENT) {
|
| + _globalState.topEventLoop.enqueue(this, kill, "kill");
|
| + return;
|
| + }
|
| + assert(priority == Isolate.BEFORE_NEXT_EVENT);
|
| + if (_scheduledControlEvents == null) {
|
| + _scheduledControlEvents = new Queue();
|
| }
|
| + _scheduledControlEvents.addLast(kill);
|
| }
|
|
|
| /**
|
| @@ -351,11 +387,18 @@ class _IsolateContext implements IsolateContext {
|
| _globalState.currentContext = this;
|
| this._setGlobals();
|
| var result = null;
|
| + _isExecutingEvent = true;
|
| try {
|
| result = code();
|
| } finally {
|
| + _isExecutingEvent = false;
|
| _globalState.currentContext = old;
|
| if (old != null) old._setGlobals();
|
| + if (_scheduledControlEvents != null) {
|
| + while (_scheduledControlEvents.isNotEmpty) {
|
| + (_scheduledControlEvents.removeFirst())();
|
| + }
|
| + }
|
| }
|
| return result;
|
| }
|
| @@ -364,6 +407,13 @@ class _IsolateContext implements IsolateContext {
|
| JS_SET_CURRENT_ISOLATE(isolateStatics);
|
| }
|
|
|
| + /**
|
| + * Handle messages comming in on the control port.
|
| + *
|
| + * These events do not go through the event queue.
|
| + * The `_globalState.currentContext` context is not set to this context
|
| + * during the handling.
|
| + */
|
| void handleControlMessage(message) {
|
| switch (message[0]) {
|
| case "pause":
|
| @@ -384,8 +434,10 @@ class _IsolateContext implements IsolateContext {
|
| case "ping":
|
| handlePing(message[1], message[2]);
|
| break;
|
| + case "kill":
|
| + handleKill(message[1], message[2]);
|
| + break;
|
| default:
|
| - print("UNKNOWN MESSAGE: $message");
|
| }
|
| }
|
|
|
| @@ -419,18 +471,30 @@ class _IsolateContext implements IsolateContext {
|
| if (ports.length - weakPorts.length > 0 || isPaused) {
|
| _globalState.isolates[id] = this; // indicate this isolate is active
|
| } else {
|
| - _shutdown();
|
| + kill();
|
| }
|
| }
|
|
|
| - void _shutdown() {
|
| + void kill() {
|
| + if (_scheduledControlEvents != null) {
|
| + // Kill all pending events.
|
| + _scheduledControlEvents.clear();
|
| + }
|
| + // Stop listening on all ports.
|
| + // This should happen before sending events to done handlers, in case
|
| + // we are listening on ourselves.
|
| + // Closes all ports, including control port.
|
| + for (var port in ports.values) {
|
| + port._close();
|
| + }
|
| + ports.clear();
|
| + weakPorts.clear();
|
| _globalState.isolates.remove(id); // indicate this isolate is not active
|
| - // Send "done" event to all listeners. This must be done after deactivating
|
| - // the current isolate, or it may get events if listening to itself.
|
| if (doneHandlers != null) {
|
| for (SendPort port in doneHandlers) {
|
| port.send(null);
|
| }
|
| + doneHandlers = null;
|
| }
|
| }
|
|
|
| @@ -1046,6 +1110,13 @@ class RawReceivePortImpl implements RawReceivePort {
|
| _handler = newHandler;
|
| }
|
|
|
| + // Close the port without unregistering it.
|
| + // Used by an isolate context to close all ports when shutting down.
|
| + void _close() {
|
| + _isClosed = true;
|
| + _handler = null;
|
| + }
|
| +
|
| void close() {
|
| if (_isClosed) return;
|
| _isClosed = true;
|
|
|