From 6abf7ffcc0de37acf98a3c8bb1f161aa9130a4d7 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Wed, 26 Oct 2016 16:56:21 -0700 Subject: [PATCH] Fix rare race condition with webworker tasks, fix minified web worker filename default --- Gruntfile.js | 23 ++++++++++++++--------- src/worker/async_proxy.js | 26 ++++++++++++++++++++------ src/worker/worker.js | 10 +++++----- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b7349352..7d6c1a05 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -79,11 +79,6 @@ module.exports = function(grunt) { 'dist/openpgp.worker.js': [ './src/worker/worker.js' ] } }, - worker_min: { - files: { - 'dist/openpgp.worker.min.js': [ './src/worker/worker.js' ] - } - }, unittests: { files: { 'test/lib/unittests-bundle.js': [ './test/unittests.js' ] @@ -110,12 +105,20 @@ module.exports = function(grunt) { to: 'OpenPGP.js v<%= pkg.version %>' }] }, + openpgp_min: { + src: ['dist/openpgp.min.js'], + dest: ['dist/openpgp.min.js'], + replacements: [{ + from: "openpgp.worker.js", + to: "openpgp.worker.min.js" + }] + }, worker_min: { src: ['dist/openpgp.worker.min.js'], dest: ['dist/openpgp.worker.min.js'], replacements: [{ - from: "importScripts('openpgp.js')", - to: "importScripts('openpgp.min.js')" + from: "openpgp.js", + to: "openpgp.min.js" }] } }, @@ -123,7 +126,7 @@ module.exports = function(grunt) { openpgp: { files: { 'dist/openpgp.min.js' : [ 'dist/openpgp.js' ], - 'dist/openpgp.worker.min.js' : [ 'dist/openpgp.worker.min.js' ] + 'dist/openpgp.worker.min.js' : [ 'dist/openpgp.worker.js' ] } }, options: { @@ -292,7 +295,9 @@ module.exports = function(grunt) { } // Build tasks - grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'replace', 'uglify']); + grunt.registerTask('version', ['replace:openpgp', 'replace:openpgp_debug']); + grunt.registerTask('replace_min', ['replace:openpgp_min', 'replace:worker_min']); + grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']); grunt.registerTask('documentation', ['jsdoc']); // Test/Dev tasks grunt.registerTask('test', ['jshint', 'jscs', 'mochaTest']); diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index 6b9ad047..a7c0d526 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -39,13 +39,24 @@ export default function AsyncProxy({ path='openpgp.worker.js', worker, config } throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')'); }; this.seedRandom(INITIAL_RANDOM_SEED); - // FIFO - this.tasks = []; + if (config) { this.worker.postMessage({ event:'configure', config }); } + + // Cannot rely on task order being maintained, use object keyed by request ID to track tasks + this.tasks = {}; + this.currentID = 0; } +/** + * Get new request ID + * @return {integer} New unique request ID +*/ +AsyncProxy.prototype.getID = function() { + return this.currentID++; +}; + /** * Message handling */ @@ -55,11 +66,12 @@ AsyncProxy.prototype.onMessage = function(event) { case 'method-return': if (msg.err) { // fail - this.tasks.shift().reject(new Error(msg.err)); + this.tasks[msg.id].reject(new Error(msg.err)); } else { // success - this.tasks.shift().resolve(msg.data); + this.tasks[msg.id].resolve(msg.data); } + delete this.tasks[msg.id]; break; case 'request-seed': this.seedRandom(RANDOM_SEED_REQUEST); @@ -106,11 +118,13 @@ AsyncProxy.prototype.terminate = function() { * @return {Promise} see the corresponding public api functions for their return types */ AsyncProxy.prototype.delegate = function(method, options) { + const id = this.getID(); + return new Promise((resolve, reject) => { // clone packets (for web worker structured cloning algorithm) - this.worker.postMessage({ event:method, options:packet.clone.clonePackets(options) }, util.getTransferables.call(util, options)); + this.worker.postMessage({ id:id, event:method, options:packet.clone.clonePackets(options) }, util.getTransferables.call(util, options)); // remember to handle parsing cloned packets from worker - this.tasks.push({ resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject }); + this.tasks[id] = { resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject }; }); }; diff --git a/src/worker/worker.js b/src/worker/worker.js index d0df34a4..62cffaea 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -44,7 +44,7 @@ self.onmessage = function(event) { break; default: - delegate(msg.event, msg.options || {}); + delegate(msg.id, msg.event, msg.options || {}); } }; @@ -75,18 +75,18 @@ function seedRandom(buffer) { * @param {String} method The public api function to be delegated to the worker thread * @param {Object} options The api function's options */ -function delegate(method, options) { +function delegate(id, method, options) { if (typeof openpgp[method] !== 'function') { - response({ event:'method-return', err:'Unknown Worker Event' }); + response({ id:id, event:'method-return', err:'Unknown Worker Event' }); return; } // parse cloned packets options = openpgp.packet.clone.parseClonedPackets(options, method); openpgp[method](options).then(function(data) { // clone packets (for web worker structured cloning algorithm) - response({ event:'method-return', data:openpgp.packet.clone.clonePackets(data) }); + response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) }); }).catch(function(e) { - response({ event:'method-return', err:e.message }); + response({ id:id, event:'method-return', err:e.message }); }); }