diff options
Diffstat (limited to 'toolkit/modules/subprocess/subprocess_worker_common.js')
-rw-r--r-- | toolkit/modules/subprocess/subprocess_worker_common.js | 211 |
1 files changed, 211 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..a0370326d7 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_worker_common.js @@ -0,0 +1,211 @@ +/* -*- 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.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), + ]); + } +} + +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: {}, + }); + }); +}; |