diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /devtools/client/shared/worker-utils.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/shared/worker-utils.js')
-rw-r--r-- | devtools/client/shared/worker-utils.js | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/devtools/client/shared/worker-utils.js b/devtools/client/shared/worker-utils.js new file mode 100644 index 0000000000..bb5c54dac3 --- /dev/null +++ b/devtools/client/shared/worker-utils.js @@ -0,0 +1,157 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ + +"use strict"; + +class WorkerDispatcher { + #msgId = 1; + #worker = null; + // Map of message ids -> promise resolution functions, for dispatching worker responses + #pendingCalls = new Map(); + #url = ""; + + constructor(url) { + this.#url = url; + } + + start() { + // When running in debugger jest test, we don't have access to ChromeWorker + if (typeof ChromeWorker == "function") { + this.#worker = new ChromeWorker(this.#url); + } else { + this.#worker = new Worker(this.#url); + } + this.#worker.onerror = err => { + console.error(`Error in worker ${this.#url}`, err.message); + }; + this.#worker.addEventListener("message", this.#onMessage); + } + + stop() { + if (!this.#worker) { + return; + } + + this.#worker.removeEventListener("message", this.#onMessage); + this.#worker.terminate(); + this.#worker = null; + this.#pendingCalls.clear(); + } + + task(method, { queue = false } = {}) { + const calls = []; + const push = args => { + return new Promise((resolve, reject) => { + if (queue && calls.length === 0) { + Promise.resolve().then(flush); + } + + calls.push({ args, resolve, reject }); + + if (!queue) { + flush(); + } + }); + }; + + const flush = () => { + const items = calls.slice(); + calls.length = 0; + + if (!this.#worker) { + this.start(); + } + + const id = this.#msgId++; + this.#worker.postMessage({ + id, + method, + calls: items.map(item => item.args), + }); + + this.#pendingCalls.set(id, items); + }; + + return (...args) => push(args); + } + + 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) { + return function (msg) { + const { id, method, calls } = msg.data; + + Promise.all( + calls.map(args => { + try { + const response = publicInterface[method].apply(undefined, args); + if (response instanceof Promise) { + return response.then( + val => ({ response: val }), + err => asErrorMessage(err) + ); + } + return { response }; + } catch (error) { + return asErrorMessage(error); + } + }) + ).then(results => { + globalThis.postMessage({ id, results }); + }); + }; +} + +function asErrorMessage(error) { + if (typeof error === "object" && error && "message" in error) { + // Error can't be sent via postMessage, so be sure to convert to + // string. + return { + error: true, + message: error.message, + metadata: error.metadata, + }; + } + + return { + error: true, + message: error == null ? error : error.toString(), + metadata: undefined, + }; +} + +// Might be loaded within a worker thread where `module` isn't available. +if (typeof module !== "undefined") { + module.exports = { + WorkerDispatcher, + workerHandler, + }; +} |