Bug 1942239 - Add option to explicitly enable incremental origin initialization in...
[gecko.git] / toolkit / modules / subprocess / subprocess_worker_common.js
blobba9c5ce7773a306ba9efb94e57bb838ea46f99ee
1 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* vim: set sts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 "use strict";
8 // This file is loaded into the same context as subprocess_unix.worker.js
9 // and subprocess_win.worker.js
10 /* import-globals-from subprocess_unix.worker.js */
12 /* exported BasePipe, BaseProcess, debug */
14 function debug(message) {
15 self.postMessage({ msg: "debug", message });
18 class BasePipe {
19 constructor() {
20 this.closing = false;
21 this.closed = false;
23 this.closedPromise = new Promise(resolve => {
24 this.resolveClosed = resolve;
25 });
27 this.pending = [];
30 shiftPending() {
31 let result = this.pending.shift();
33 if (this.closing && !this.pending.length) {
34 this.close();
37 return result;
41 let nextProcessId = 0;
43 class BaseProcess {
44 constructor(options) {
45 this.id = nextProcessId++;
47 this.exitCode = null;
49 this.exitPromise = new Promise(resolve => {
50 this.resolveExit = resolve;
51 });
52 this.exitPromise.then(() => {
53 // The input file descriptors will be closed after poll
54 // reports that their input buffers are empty. If we close
55 // them now, we may lose output.
56 this.pipes[0].close(true);
57 });
59 this.pid = null;
60 this.pipes = [];
62 this.spawn(options);
65 /**
66 * Waits for the process to exit and all of its pending IO operations to
67 * complete.
69 * @returns {Promise<void>}
71 awaitFinished() {
72 return Promise.all([
73 this.exitPromise,
74 ...this.pipes.map(pipe => pipe.closedPromise),
75 ]);
79 let requests = {
80 init(details) {
81 io.init(details);
83 return { data: {} };
86 shutdown() {
87 io.shutdown();
89 return { data: {} };
92 close(pipeId, force = false) {
93 let pipe = io.getPipe(pipeId);
95 return pipe.close(force).then(() => ({ data: {} }));
98 spawn(options) {
99 let process = new Process(options);
100 let processId = process.id;
102 io.addProcess(process);
104 let fds = process.pipes.map(pipe => pipe.id);
106 return { data: { processId, fds, pid: process.pid } };
109 connectRunning(options) {
110 let process = new ManagedProcess(options);
111 let processId = process.id;
113 io.addProcess(process);
115 return { data: { processId, fds: process.pipes.map(pipe => pipe.id) } };
118 kill(processId, force = false) {
119 let process = io.getProcess(processId);
121 process.kill(force ? 9 : 15);
123 return { data: {} };
126 wait(processId) {
127 let process = io.getProcess(processId);
129 process.wait();
131 process.awaitFinished().then(() => {
132 io.cleanupProcess(process);
135 return process.exitPromise.then(exitCode => {
136 return { data: { exitCode } };
140 read(pipeId, count) {
141 let pipe = io.getPipe(pipeId);
143 return pipe.read(count).then(buffer => {
144 return { data: { buffer } };
148 write(pipeId, buffer) {
149 let pipe = io.getPipe(pipeId);
151 return pipe.write(buffer).then(bytesWritten => {
152 return { data: { bytesWritten } };
156 getOpenFiles() {
157 return { data: new Set(io.pipes.keys()) };
160 getProcesses() {
161 let data = new Map(
162 Array.from(io.processes.values())
163 .filter(proc => proc.exitCode == null)
164 .map(proc => [proc.id, proc.pid])
166 return { data };
169 waitForNoProcesses() {
170 return Promise.all(
171 Array.from(io.processes.values(), proc => proc.awaitFinished())
175 // It is the caller's responsability to make sure dup() is called on the FDs
176 // returned here.
177 getFds(processId) {
178 // fd is a unix.Fd aka CDataFinalizer that wraps the actual integer. We can
179 // retrieve its value via .toString(), if it has not been closed yet.
180 let process = io.getProcess(processId);
181 let pipes = process.pipes.map(p => parseInt(p.fd.toString(), 10));
182 return {
183 data: [pipes[0], pipes[1], pipes[2]],
188 onmessage = event => {
189 io.messageCount--;
191 let { msg, msgId, args } = event.data;
193 new Promise(resolve => {
194 resolve(requests[msg](...args));
196 .then(result => {
197 let response = {
198 msg: "success",
199 msgId,
200 data: result.data,
203 self.postMessage(response, result.transfer || []);
205 .catch(error => {
206 if (error instanceof Error) {
207 error = {
208 message: error.message,
209 fileName: error.fileName,
210 lineNumber: error.lineNumber,
211 column: error.column,
212 stack: error.stack,
213 errorCode: error.errorCode,
217 self.postMessage({
218 msg: "failure",
219 msgId,
220 error,
223 .catch(error => {
224 console.error(error);
226 self.postMessage({
227 msg: "failure",
228 msgId,
229 error: {},