diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/modules/subprocess/subprocess_worker_common.js | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/modules/subprocess/subprocess_worker_common.js')
-rw-r--r-- | toolkit/modules/subprocess/subprocess_worker_common.js | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/toolkit/modules/subprocess/subprocess_worker_common.js b/toolkit/modules/subprocess/subprocess_worker_common.js new file mode 100644 index 0000000000..3dbc420c7c --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_worker_common.js @@ -0,0 +1,238 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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"; + +// This file is loaded into the same context as subprocess_worker_unix.js +// and subprocess_worker_win.js +/* import-globals-from subprocess_worker_unix.js */ + +/* exported BasePipe, BaseProcess, debug */ + +function debug(message) { + self.postMessage({ msg: "debug", message }); +} + +class BasePipe { + constructor() { + this.closing = false; + this.closed = false; + + this.closedPromise = new Promise(resolve => { + this.resolveClosed = resolve; + }); + + this.pending = []; + } + + shiftPending() { + let result = this.pending.shift(); + + if (this.closing && !this.pending.length) { + this.close(); + } + + return result; + } +} + +let nextProcessId = 0; + +class BaseProcess { + constructor(options) { + this.id = nextProcessId++; + + this.exitCode = null; + + this.exitPromise = new Promise(resolve => { + this.resolveExit = resolve; + }); + this.exitPromise.then(() => { + // The input file descriptors will be closed after poll + // reports that their input buffers are empty. If we close + // them now, we may lose output. + this.pipes[0].close(true); + }); + + this.pid = null; + this.pipes = []; + + this.stringArrays = []; + + this.spawn(options); + } + + /** + * Waits for the process to exit and all of its pending IO operations to + * complete. + * + * @returns {Promise<void>} + */ + awaitFinished() { + return Promise.all([ + this.exitPromise, + ...this.pipes.map(pipe => pipe.closedPromise), + ]); + } + + /** + * Creates a null-terminated array of pointers to null-terminated C-strings, + * and returns it. + * + * @param {string[]} strings + * The strings to convert into a C string array. + * + * @returns {ctypes.char.ptr.array} + */ + stringArray(strings) { + let result = ctypes.char.ptr.array(strings.length + 1)(); + + let cstrings = strings.map(str => ctypes.char.array()(str)); + for (let [i, cstring] of cstrings.entries()) { + result[i] = cstring; + } + + // Char arrays used in char arg and environment vectors must be + // explicitly kept alive in a JS object, or they will be reaped + // by the GC if it runs before our process is started. + this.stringArrays.push(cstrings); + + return result; + } +} + +let requests = { + init(details) { + io.init(details); + + return { data: {} }; + }, + + shutdown() { + io.shutdown(); + + return { data: {} }; + }, + + close(pipeId, force = false) { + let pipe = io.getPipe(pipeId); + + return pipe.close(force).then(() => ({ data: {} })); + }, + + spawn(options) { + let process = new Process(options); + let processId = process.id; + + io.addProcess(process); + + let fds = process.pipes.map(pipe => pipe.id); + + return { data: { processId, fds, pid: process.pid } }; + }, + + kill(processId, force = false) { + let process = io.getProcess(processId); + + process.kill(force ? 9 : 15); + + return { data: {} }; + }, + + wait(processId) { + let process = io.getProcess(processId); + + process.wait(); + + process.awaitFinished().then(() => { + io.cleanupProcess(process); + }); + + return process.exitPromise.then(exitCode => { + return { data: { exitCode } }; + }); + }, + + read(pipeId, count) { + let pipe = io.getPipe(pipeId); + + return pipe.read(count).then(buffer => { + return { data: { buffer } }; + }); + }, + + write(pipeId, buffer) { + let pipe = io.getPipe(pipeId); + + return pipe.write(buffer).then(bytesWritten => { + return { data: { bytesWritten } }; + }); + }, + + getOpenFiles() { + return { data: new Set(io.pipes.keys()) }; + }, + + getProcesses() { + let data = new Map( + Array.from(io.processes.values()) + .filter(proc => proc.exitCode == null) + .map(proc => [proc.id, proc.pid]) + ); + return { data }; + }, + + waitForNoProcesses() { + return Promise.all( + Array.from(io.processes.values(), proc => proc.awaitFinished()) + ); + }, +}; + +onmessage = event => { + io.messageCount--; + + let { msg, msgId, args } = event.data; + + new Promise(resolve => { + resolve(requests[msg](...args)); + }) + .then(result => { + let response = { + msg: "success", + msgId, + data: result.data, + }; + + self.postMessage(response, result.transfer || []); + }) + .catch(error => { + if (error instanceof Error) { + error = { + message: error.message, + fileName: error.fileName, + lineNumber: error.lineNumber, + column: error.column, + stack: error.stack, + errorCode: error.errorCode, + }; + } + + self.postMessage({ + msg: "failure", + msgId, + error, + }); + }) + .catch(error => { + console.error(error); + + self.postMessage({ + msg: "failure", + msgId, + error: {}, + }); + }); +}; |