fix(launcher): ensure that local browser launch waits for process exit (#489)
This commit is contained in:
parent
6ae6143711
commit
9b46014493
|
|
@ -67,7 +67,10 @@ export class CRBrowserServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(): Promise<CRBrowser> {
|
async connect(): Promise<CRBrowser> {
|
||||||
return CRBrowser.connect(this._connectOptions);
|
const browser = await CRBrowser.connect(this._connectOptions);
|
||||||
|
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
|
||||||
|
browser.close = this._gracefullyClose;
|
||||||
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
process(): ChildProcess {
|
process(): ChildProcess {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,10 @@ export class FFBrowserServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(): Promise<FFBrowser> {
|
async connect(): Promise<FFBrowser> {
|
||||||
return FFBrowser.connect(this._connectOptions);
|
const browser = await FFBrowser.connect(this._connectOptions);
|
||||||
|
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
|
||||||
|
browser.close = this._gracefullyClose;
|
||||||
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
process(): ChildProcess {
|
process(): ChildProcess {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import * as readline from 'readline';
|
||||||
import { TimeoutError } from '../errors';
|
import { TimeoutError } from '../errors';
|
||||||
import * as platform from '../platform';
|
import * as platform from '../platform';
|
||||||
|
|
||||||
|
const debugLauncher = platform.debug('pw:launcher');
|
||||||
const removeFolderAsync = platform.promisify(removeFolder);
|
const removeFolderAsync = platform.promisify(removeFolder);
|
||||||
|
|
||||||
export type LaunchProcessOptions = {
|
export type LaunchProcessOptions = {
|
||||||
|
|
@ -43,7 +44,10 @@ export type LaunchProcessOptions = {
|
||||||
|
|
||||||
type LaunchResult = { launchedProcess: childProcess.ChildProcess, gracefullyClose: () => Promise<void> };
|
type LaunchResult = { launchedProcess: childProcess.ChildProcess, gracefullyClose: () => Promise<void> };
|
||||||
|
|
||||||
|
let lastLaunchedId = 0;
|
||||||
|
|
||||||
export async function launchProcess(options: LaunchProcessOptions): Promise<LaunchResult> {
|
export async function launchProcess(options: LaunchProcessOptions): Promise<LaunchResult> {
|
||||||
|
const id = ++lastLaunchedId;
|
||||||
let stdio: ('ignore' | 'pipe')[] = ['pipe', 'pipe', 'pipe'];
|
let stdio: ('ignore' | 'pipe')[] = ['pipe', 'pipe', 'pipe'];
|
||||||
if (options.pipe) {
|
if (options.pipe) {
|
||||||
if (options.dumpio)
|
if (options.dumpio)
|
||||||
|
|
@ -63,6 +67,7 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
||||||
stdio
|
stdio
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
debugLauncher(`[${id}] <launching> ${options.executablePath} ${options.args.join(' ')}`);
|
||||||
|
|
||||||
if (!spawnedProcess.pid) {
|
if (!spawnedProcess.pid) {
|
||||||
let reject: (e: Error) => void;
|
let reject: (e: Error) => void;
|
||||||
|
|
@ -81,13 +86,14 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
||||||
let processClosed = false;
|
let processClosed = false;
|
||||||
const waitForProcessToClose = new Promise((fulfill, reject) => {
|
const waitForProcessToClose = new Promise((fulfill, reject) => {
|
||||||
spawnedProcess.once('exit', () => {
|
spawnedProcess.once('exit', () => {
|
||||||
|
debugLauncher(`[${id}] <process did exit>`);
|
||||||
processClosed = true;
|
processClosed = true;
|
||||||
helper.removeEventListeners(listeners);
|
helper.removeEventListeners(listeners);
|
||||||
// Cleanup as processes exit.
|
// Cleanup as processes exit.
|
||||||
if (options.tempDir) {
|
if (options.tempDir) {
|
||||||
removeFolderAsync(options.tempDir)
|
removeFolderAsync(options.tempDir)
|
||||||
.then(() => fulfill())
|
.catch((err: Error) => console.error(err))
|
||||||
.catch((err: Error) => console.error(err));
|
.then(fulfill);
|
||||||
} else {
|
} else {
|
||||||
fulfill();
|
fulfill();
|
||||||
}
|
}
|
||||||
|
|
@ -111,14 +117,17 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
||||||
if (gracefullyClosing)
|
if (gracefullyClosing)
|
||||||
return;
|
return;
|
||||||
gracefullyClosing = true;
|
gracefullyClosing = true;
|
||||||
|
debugLauncher(`[${id}] <gracefully close start>`);
|
||||||
options.attemptToGracefullyClose().catch(() => killProcess());
|
options.attemptToGracefullyClose().catch(() => killProcess());
|
||||||
// TODO: forcefully kill the process after some timeout.
|
// TODO: forcefully kill the process after some timeout.
|
||||||
await waitForProcessToClose;
|
await waitForProcessToClose;
|
||||||
|
debugLauncher(`[${id}] <gracefully close end>`);
|
||||||
helper.removeEventListeners(listeners);
|
helper.removeEventListeners(listeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method has to be sync to be used as 'exit' event handler.
|
// This method has to be sync to be used as 'exit' event handler.
|
||||||
function killProcess() {
|
function killProcess() {
|
||||||
|
debugLauncher(`[${id}] <kill>`);
|
||||||
helper.removeEventListeners(listeners);
|
helper.removeEventListeners(listeners);
|
||||||
if (spawnedProcess.pid && !spawnedProcess.killed && !processClosed) {
|
if (spawnedProcess.pid && !spawnedProcess.killed && !processClosed) {
|
||||||
// Force kill chrome.
|
// Force kill chrome.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,10 @@ export class WKBrowserServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(): Promise<WKBrowser> {
|
async connect(): Promise<WKBrowser> {
|
||||||
return WKBrowser.connect(this._connectOptions);
|
const browser = await WKBrowser.connect(this._connectOptions);
|
||||||
|
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
|
||||||
|
browser.close = this._gracefullyClose;
|
||||||
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
process(): ChildProcess {
|
process(): ChildProcess {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue