Bug 1714082 - Only attach one message listener to devtools workers r=nchevobbe

Previously, a separate listener was added for every message, and all of them
were called for every response.

Differential Revision: https://phabricator.services.mozilla.com/D115312
This commit is contained in:
wartmanm 2021-06-21 08:25:24 +00:00
parent abbbf94915
commit c92df274ed
8 changed files with 308 additions and 208 deletions

View File

@ -7750,7 +7750,10 @@ module.exports = networkRequest;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -7760,6 +7763,8 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -7767,8 +7772,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
@ -7782,7 +7789,11 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
@ -7802,35 +7813,9 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0])
calls: items.map(item => item.args)
});
const listener = ({
data: result
}) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -7838,6 +7823,36 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};

View File

@ -559,7 +559,10 @@ module.exports = networkRequest;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -569,6 +572,8 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -576,8 +581,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
@ -591,7 +598,11 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
@ -611,35 +622,9 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0])
calls: items.map(item => item.args)
});
const listener = ({
data: result
}) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -647,6 +632,36 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};

View File

@ -220,7 +220,10 @@ module.exports = networkRequest;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -230,6 +233,8 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -237,8 +242,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
@ -252,7 +259,11 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
@ -272,35 +283,9 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0])
calls: items.map(item => item.args)
});
const listener = ({
data: result
}) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -308,6 +293,36 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};

View File

@ -156,7 +156,10 @@ module.exports = networkRequest;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -166,6 +169,8 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -173,8 +178,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
@ -188,7 +195,11 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
@ -208,35 +219,9 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0])
calls: items.map(item => item.args)
});
const listener = ({
data: result
}) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -244,6 +229,36 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};

View File

@ -7,7 +7,9 @@ const { WorkerDispatcher, workerHandler } = require("../worker-utils");
describe("worker utils", () => {
it("starts a worker", () => {
const dispatcher = new WorkerDispatcher();
global.Worker = jest.fn();
global.Worker = jest.fn(function() {
this.addEventListener = jest.fn();
});
dispatcher.start("foo");
expect(dispatcher.worker).toEqual(global.Worker.mock.instances[0]);
});
@ -17,6 +19,8 @@ describe("worker utils", () => {
const terminateMock = jest.fn();
global.Worker = jest.fn(() => ({
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
terminate: terminateMock,
}));

View File

@ -5,6 +5,9 @@
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
// Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -13,6 +16,7 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -20,8 +24,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, { queue = false } = {}) {
@ -32,7 +38,7 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({ args, resolve, reject });
if (!queue) {
flush();
@ -52,34 +58,10 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0]),
calls: items.map(item => item.args),
});
const listener = ({ data: result }) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -88,6 +70,30 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({ data: result }) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const { resolve, reject } = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
},
};
function workerHandler(publicInterface) {

View File

@ -135,7 +135,10 @@ module.exports = networkRequest;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -145,6 +148,8 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -152,8 +157,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
@ -167,7 +174,11 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
@ -187,35 +198,9 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0])
calls: items.map(item => item.args)
});
const listener = ({
data: result
}) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -223,6 +208,36 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};

View File

@ -183,7 +183,10 @@ module.exports = networkRequest;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
@ -193,6 +196,8 @@ WorkerDispatcher.prototype = {
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
@ -200,8 +205,10 @@ WorkerDispatcher.prototype = {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
@ -215,7 +222,11 @@ WorkerDispatcher.prototype = {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
@ -235,35 +246,9 @@ WorkerDispatcher.prototype = {
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0])
calls: items.map(item => item.args)
});
const listener = ({
data: result
}) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
@ -271,6 +256,36 @@ WorkerDispatcher.prototype = {
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};