Skip to content
This repository has been archived by the owner on Dec 6, 2022. It is now read-only.

Commit

Permalink
Merge pull request #703 from mrcrane/killchromeonwindows
Browse files Browse the repository at this point in the history
Fix race condition when killing Chrome on Windows when disconnecting
  • Loading branch information
roblourens authored Jul 18, 2018
2 parents 734870a + e85978f commit 83decfc
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 19 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 41 additions & 18 deletions src/chromeDebugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,32 +312,17 @@ export class ChromeDebugAdapter extends CoreDebugAdapter {
super.onResumed();
}

public disconnect(args: DebugProtocol.DisconnectArguments): void {
public async disconnect(args: DebugProtocol.DisconnectArguments): Promise<void> {
const hadTerminated = this._hasTerminated;

// Disconnect before killing Chrome, because running "taskkill" when it's paused sometimes doesn't kill it
super.disconnect(args);

if ( (this._chromeProc || (!this._chromeProc && this._chromePID)) && !hadTerminated) {
if ( (this._chromeProc || this._chromePID) && !hadTerminated) {
// Only kill Chrome if the 'disconnect' originated from vscode. If we previously terminated
// due to Chrome shutting down, or devtools taking over, don't kill Chrome.
if (coreUtils.getPlatform() === coreUtils.Platform.Windows && this._chromePID) {
let taskkillCmd = `taskkill /PID ${this._chromePID}`;
logger.log(`Killing Chrome process by pid: ${taskkillCmd}`);
try {
// Run synchronously because this process may be killed before exec() would run
execSync(taskkillCmd);
} catch (e) {
// Can fail if Chrome was already open, and the process with _chromePID is gone.
// Or if it already shut down for some reason.
}
// execSync above may succeed, but Chrome still might not shut down, for example if the web page promts the user about unsaved changes.
// In that case, we need to use /F to force shutdown, but we risk Chrome not shutting down correctly.
taskkillCmd = `taskkill /F /PID ${this._chromePID}`;
logger.log(`Killing Chrome process by pid (using force in case the first attempt failed): ${taskkillCmd}`);
try {
execSync(taskkillCmd);
} catch (e) {}
await this.killChromeOnWindows(this._chromePID);
} else if (this._chromeProc) {
logger.log('Killing Chrome process');
this._chromeProc.kill('SIGINT');
Expand All @@ -347,6 +332,44 @@ export class ChromeDebugAdapter extends CoreDebugAdapter {
this._chromeProc = null;
}

private async killChromeOnWindows(chromePID: number): Promise<void> {
let taskkillCmd = `taskkill /PID ${chromePID}`;
logger.log(`Killing Chrome process by pid: ${taskkillCmd}`);
try {
execSync(taskkillCmd);
} catch (e) {
// The command will fail if process was not found. This can be safely ignored.
}

for (let i = 0 ; i < 10; i++) {
// Check to see if the process is still running, with CSV output format
let tasklistCmd = `tasklist /FI "PID eq ${chromePID}" /FO CSV`;
logger.log(`Looking up process by pid: ${tasklistCmd}`);
let tasklistOutput = execSync(tasklistCmd).toString();

// If the process is found, tasklist will output CSV with one of the values being the PID. Exit code will be 0.
// If the process is not found, tasklist will give a generic "not found" message instead. Exit code will also be 0.
// If we see an entry in the CSV for the PID, then we can assume the process was found.
if (!tasklistOutput.includes(`"${chromePID}"`)) {
logger.log(`Chrome process with pid ${chromePID} is not running`);
return;
}

// Give the process some time to close gracefully
logger.log(`Chrome process with pid ${chromePID} is still alive, waiting...`);
await new Promise<void>((resolve) => {
setTimeout(resolve, 200);
});
}

// At this point we can assume the process won't close on its own, so force kill it
let taskkillForceCmd = `taskkill /F /PID ${chromePID}`;
logger.log(`Killing Chrome process timed out. Killing again using force: ${taskkillForceCmd}`);
try {
execSync(taskkillForceCmd);
} catch (e) {}
}

/**
* Opt-in event called when the 'reload' button in the debug widget is pressed
*/
Expand Down

1 comment on commit 83decfc

@shevyallan
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A

Please sign in to comment.