Handle kernel messages synchronously

A problem can happen when two messages come in for different
comms, where the second depends on the first (for example, the
first might be a message setting the state of a widget, and the
second triggering a view creation for the widget).  Since comm
message queues are independent of each other, the second message
could be executed before the first message.

This exposes a more fundamental assumption users are likely to
have that messages from python are processed synchronously.

Thanks to @dmadeka for reporting an error that led to discovering this issue.
This commit is contained in:
Jason Grout 2015-02-25 18:47:28 +00:00
parent 2eb028bcec
commit e1a6dbd4be
2 changed files with 18 additions and 8 deletions

View File

@ -117,6 +117,7 @@ define([
// don't return a comm, so that further .then() functions
// get an undefined comm input
});
return this.comms[content.comm_id];
};
CommManager.prototype.comm_msg = function(msg) {
@ -134,6 +135,7 @@ define([
}
return comm;
});
return this.comms[content.comm_id];
};
//-----------------------------------------------------------------------

View File

@ -855,22 +855,23 @@ define([
};
Kernel.prototype._handle_ws_message = function (e) {
var that = this;
this._msg_queue = this._msg_queue.then(function() {
return serialize.deserialize(e.data);
}).then($.proxy(this._finish_ws_message, this))
}).then(function(msg) {return that._finish_ws_message(msg);})
.catch(utils.reject("Couldn't process kernel message", true));
};
Kernel.prototype._finish_ws_message = function (msg) {
switch (msg.channel) {
case 'shell':
this._handle_shell_reply(msg);
return this._handle_shell_reply(msg);
break;
case 'iopub':
this._handle_iopub_message(msg);
return this._handle_iopub_message(msg);
break;
case 'stdin':
this._handle_input_request(msg);
return this._handle_input_request(msg);
break;
default:
console.error("unrecognized message channel", msg.channel, msg);
@ -879,10 +880,12 @@ define([
Kernel.prototype._handle_shell_reply = function (reply) {
this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
var that = this;
var content = reply.content;
var metadata = reply.metadata;
var parent_id = reply.parent_header.msg_id;
var callbacks = this.get_callbacks_for_msg(parent_id);
var promise = Promise.resolve();
if (!callbacks || !callbacks.shell) {
return;
}
@ -892,17 +895,21 @@ define([
this._finish_shell(parent_id);
if (shell_callbacks.reply !== undefined) {
shell_callbacks.reply(reply);
promise = promise.then(function() {return shell_callbacks.reply(reply)});
}
if (content.payload && shell_callbacks.payload) {
this._handle_payloads(content.payload, shell_callbacks.payload, reply);
promise = promise.then(function() {
return that._handle_payloads(content.payload, shell_callbacks.payload, reply);
});
}
return promise;
};
/**
* @function _handle_payloads
*/
Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
var promise = [];
var l = payloads.length;
// Payloads are handled by triggering events because we don't want the Kernel
// to depend on the Notebook or Pager classes.
@ -910,9 +917,10 @@ define([
var payload = payloads[i];
var callback = payload_callbacks[payload.source];
if (callback) {
callback(payload, msg);
promise.push(callback(payload, msg));
}
}
return Promise.all(promise);
};
/**
@ -1025,7 +1033,7 @@ define([
Kernel.prototype._handle_iopub_message = function (msg) {
var handler = this.get_iopub_handler(msg.header.msg_type);
if (handler !== undefined) {
handler(msg);
return handler(msg);
}
};