From 834cd9c43db06a777f4bb47f19eaf7fa1a41b8e8 2015-02-13 22:41:16 From: Jason Grout Date: 2015-02-13 22:41:16 Subject: [PATCH] Fix race condition in javascript kernel message processing Because the binary messages are now deserialized using the asynchronous FileReader API, we need to have some way to force the messages to still be processed in the order they are received. This patch implements a simple processing queue using promises. --- diff --git a/IPython/html/static/services/kernels/kernel.js b/IPython/html/static/services/kernels/kernel.js index f414507..42478ae 100644 --- a/IPython/html/static/services/kernels/kernel.js +++ b/IPython/html/static/services/kernels/kernel.js @@ -41,6 +41,7 @@ define([ this.username = "username"; this.session_id = utils.uuid(); this._msg_callbacks = {}; + this._msg_queue = Promise.resolve(); this.info_reply = {}; // kernel_info_reply stored here after starting if (typeof(WebSocket) !== 'undefined') { @@ -854,7 +855,10 @@ define([ }; Kernel.prototype._handle_ws_message = function (e) { - serialize.deserialize(e.data, $.proxy(this._finish_ws_message, this)); + this._msg_queue = this._msg_queue.then(function() { + return serialize.deserialize(e.data); + }).then($.proxy(this._finish_ws_message, this)) + .catch(utils.reject("Couldn't process kernel message", true)); }; Kernel.prototype._finish_ws_message = function (msg) { diff --git a/IPython/html/static/services/kernels/serialize.js b/IPython/html/static/services/kernels/serialize.js index 4da4c9a..9fb8d28 100644 --- a/IPython/html/static/services/kernels/serialize.js +++ b/IPython/html/static/services/kernels/serialize.js @@ -30,7 +30,7 @@ define([ return msg; }; - var _deserialize_binary = function(data, callback) { + var _deserialize_binary = function(data) { /** * deserialize the binary message format * callback will be called with a message whose buffers attribute @@ -39,28 +39,31 @@ define([ if (data instanceof Blob) { // data is Blob, have to deserialize from ArrayBuffer in reader callback var reader = new FileReader(); - reader.onload = function () { - var msg = _deserialize_array_buffer(this.result); - callback(msg); - }; + var promise = new Promise(function(resolve, reject) { + reader.onload = function () { + var msg = _deserialize_array_buffer(this.result); + resolve(msg); + }; + }); reader.readAsArrayBuffer(data); + return promise; } else { // data is ArrayBuffer, can deserialize directly var msg = _deserialize_array_buffer(data); - callback(msg); + return msg; } }; - var deserialize = function (data, callback) { + var deserialize = function (data) { /** - * deserialize a message and pass the unpacked message object to callback + * deserialize a message and return a promise for the unpacked message */ if (typeof data === "string") { // text JSON message - callback(JSON.parse(data)); + return Promise.resolve(JSON.parse(data)); } else { // binary message - _deserialize_binary(data, callback); + return Promise.resolve(_deserialize_binary(data)); } }; @@ -117,4 +120,4 @@ define([ serialize: serialize }; return exports; -}); \ No newline at end of file +});