mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-01 01:38:07 +08:00
Change-Id: I9af281bc40223a5ceb2a6f71ce247970beea4b02 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2116050 Commit-Queue: Paul Lewis <aerotwist@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
197 lines
4.8 KiB
TypeScript
197 lines
4.8 KiB
TypeScript
// Copyright 2020 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/* eslint-disable no-console */
|
|
// no-console disabled here as this is a test runner and expects to output to the console
|
|
|
|
import {ChildProcessWithoutNullStreams, spawn} from 'child_process';
|
|
import * as cluster from 'cluster';
|
|
import {join} from 'path';
|
|
import {WorkerMessage} from './worker-message.js';
|
|
|
|
let envJobs = Number(process.env['JOBS']);
|
|
if (Number.isNaN(envJobs) || envJobs < 1) {
|
|
envJobs = 4;
|
|
}
|
|
|
|
// Multiple workers in debug mode is difficult to manage, so drop to a single worker.
|
|
const envDebug = !!process.env['DEBUG'];
|
|
if (envDebug) {
|
|
envJobs = 1;
|
|
}
|
|
|
|
const workerCount = envJobs;
|
|
const taskList: string[] = [];
|
|
const ports = new Map<cluster.Worker, number>();
|
|
|
|
export function purge() {
|
|
taskList.length = 0;
|
|
}
|
|
|
|
export function killWorkers() {
|
|
const workerNames = Object.keys(cluster.workers);
|
|
for (const workerName of workerNames) {
|
|
const worker = cluster.workers[workerName];
|
|
if (!worker) {
|
|
continue;
|
|
}
|
|
|
|
worker.process.kill('SIGTERM');
|
|
}
|
|
}
|
|
|
|
export function enqueue(tasks: string[]) {
|
|
for (const task of tasks) {
|
|
taskList.push(task);
|
|
}
|
|
|
|
// Wake up the workers.
|
|
const workerNames = Object.keys(cluster.workers);
|
|
for (const workerName of workerNames) {
|
|
const worker = cluster.workers[workerName];
|
|
if (!worker) {
|
|
continue;
|
|
}
|
|
|
|
worker.send({ping: true});
|
|
}
|
|
}
|
|
|
|
let hostedModeServer: ChildProcessWithoutNullStreams;
|
|
let exitCode = 0;
|
|
let finishCount = 0;
|
|
export function init() {
|
|
finishCount = workerCount;
|
|
console.log(`Creating ${workerCount} worker${(workerCount === 1 ? '' : 's')}.`);
|
|
|
|
// Silence the stdout of individual workers.
|
|
cluster.settings.silent = true;
|
|
|
|
for (let w = 0; w < workerCount; w++) {
|
|
const worker = cluster.fork({silent: true});
|
|
worker.on('message', onWorkerMessage);
|
|
worker.on('exit', onWorkerDisconnect);
|
|
|
|
ports.set(worker, 9222 + w);
|
|
}
|
|
|
|
console.log('Spawning hosted mode server');
|
|
const serverScriptPath = join(__dirname, '..', '..', 'scripts', 'hosted_mode', 'server.js');
|
|
const cwd = join(__dirname, '..', '..');
|
|
const {execPath} = process;
|
|
hostedModeServer = spawn(execPath, [serverScriptPath], {cwd});
|
|
hostedModeServer.on('error', handleHostedModeError);
|
|
hostedModeServer.stderr.on('data', handleHostedModeError);
|
|
|
|
// Debug mode: write out server responses.
|
|
if (envDebug) {
|
|
hostedModeServer.stdout.on('data', (message: any) => {
|
|
console.log(`Hosted mode server: ${message}`);
|
|
});
|
|
}
|
|
}
|
|
|
|
function handleHostedModeError(data: Error) {
|
|
console.log(`Hosted mode server: ${data}`);
|
|
interruptionHandler();
|
|
}
|
|
|
|
export function interruptionHandler() {
|
|
exitCode = 1;
|
|
shutdown();
|
|
}
|
|
|
|
function shutdown() {
|
|
killWorkers();
|
|
|
|
console.log('\n');
|
|
console.log('Stopping hosted mode server');
|
|
hostedModeServer.kill();
|
|
|
|
console.log(`Exiting with status code ${exitCode}`);
|
|
process.exit(exitCode);
|
|
}
|
|
|
|
function locateWorkerByPid(pid: number): cluster.Worker {
|
|
const workerName = Object.keys(cluster.workers).find(key => {
|
|
const worker = cluster.workers[key];
|
|
if (!worker) {
|
|
return;
|
|
}
|
|
|
|
return worker.process.pid === pid;
|
|
});
|
|
|
|
if (!workerName) {
|
|
throw new Error(`Received message from pid ${pid}, but was unable to find a corresponding worker`);
|
|
}
|
|
|
|
return cluster.workers[workerName] as cluster.Worker;
|
|
}
|
|
|
|
const envVerbose = !!process.env['VERBOSE'];
|
|
function log(pid: number, msg: string, alwaysShow = false) {
|
|
const showMessages = envVerbose || alwaysShow;
|
|
if (!showMessages) {
|
|
return;
|
|
}
|
|
|
|
console.log(`Worker (${pid}): ${msg}`);
|
|
}
|
|
|
|
function onWorkerMessage({pid, details}: {pid: number, details: WorkerMessage}) {
|
|
const worker = locateWorkerByPid(pid);
|
|
|
|
switch (details) {
|
|
case 'requestTask': {
|
|
const task = taskList.shift();
|
|
if (!task) {
|
|
log(pid, 'Finished');
|
|
finishCount--;
|
|
if (finishCount === 0 && !envDebug) {
|
|
shutdown();
|
|
}
|
|
break;
|
|
}
|
|
|
|
log(pid, `Requested task, being given ${task}`);
|
|
worker.send({
|
|
type: 'task',
|
|
task,
|
|
});
|
|
break;
|
|
}
|
|
|
|
case 'notifyInit': {
|
|
log(pid, 'Ready - Sending port');
|
|
worker.send({
|
|
port: ports.get(worker),
|
|
});
|
|
break;
|
|
}
|
|
|
|
case 'failure': {
|
|
log(pid, 'Failure');
|
|
exitCode = 1;
|
|
break;
|
|
}
|
|
|
|
// All other messages, e.g. pass/fail from tests.
|
|
default: {
|
|
console.log(details);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function onWorkerDisconnect(code: number, signal: NodeJS.Signals) {
|
|
if (signal) {
|
|
console.log('Worker killed by ' + signal);
|
|
} else if (code) {
|
|
console.log('Worker exited with code ' + code);
|
|
} else {
|
|
console.log('Worker exited successfully');
|
|
}
|
|
}
|