Skip to content

Commit 1adce66

Browse files
committed
fix: webpack process leaks
1 parent 96cb477 commit 1adce66

File tree

8 files changed

+80
-15
lines changed

8 files changed

+80
-15
lines changed

lib/bash-scripts/terminateProcess.sh

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
3+
terminateTree() {
4+
for cpid in $(/usr/bin/pgrep -P $1); do
5+
terminateTree $cpid
6+
done
7+
kill -9 $1 > /dev/null 2>&1
8+
}
9+
10+
for pid in $*; do
11+
terminateTree $pid
12+
done

lib/controllers/prepare-controller.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class PrepareController extends EventEmitter {
5454
return result;
5555
}
5656

57-
public stopWatchers(projectDir: string, platform: string): void {
57+
public async stopWatchers(projectDir: string, platform: string): Promise<void> {
5858
const platformLowerCase = platform.toLowerCase();
5959

6060
if (this.watchersData && this.watchersData[projectDir] && this.watchersData[projectDir][platformLowerCase] && this.watchersData[projectDir][platformLowerCase].nativeFilesWatcher) {
@@ -63,7 +63,7 @@ export class PrepareController extends EventEmitter {
6363
}
6464

6565
if (this.watchersData && this.watchersData[projectDir] && this.watchersData[projectDir][platformLowerCase] && this.watchersData[projectDir][platformLowerCase].webpackCompilerProcess) {
66-
this.$webpackCompilerService.stopWebpackCompiler(platform);
66+
await this.$webpackCompilerService.stopWebpackCompiler(platform);
6767
this.watchersData[projectDir][platformLowerCase].webpackCompilerProcess = null;
6868
}
6969
}

lib/controllers/run-controller.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,23 @@ export class RunController extends EventEmitter implements IRunController {
7070
.map(descriptor => descriptor.identifier);
7171

7272
// Handle the case when no more devices left for any of the persisted platforms
73-
_.each(liveSyncProcessInfo.platforms, platform => {
73+
for (let i = 0; i < liveSyncProcessInfo.platforms.length; i++) {
74+
const platform = liveSyncProcessInfo.platforms[i];
7475
const devices = this.$devicesService.getDevicesForPlatform(platform);
7576
if (!devices || !devices.length) {
76-
this.$prepareController.stopWatchers(projectDir, platform);
77+
await this.$prepareController.stopWatchers(projectDir, platform);
7778
}
78-
});
79+
}
7980

8081
// In case deviceIdentifiers are not passed, we should stop the whole LiveSync.
8182
if (!deviceIdentifiers || !deviceIdentifiers.length || !liveSyncProcessInfo.deviceDescriptors || !liveSyncProcessInfo.deviceDescriptors.length) {
8283
if (liveSyncProcessInfo.timer) {
8384
clearTimeout(liveSyncProcessInfo.timer);
8485
}
8586

86-
_.each(liveSyncProcessInfo.platforms, platform => {
87-
this.$prepareController.stopWatchers(projectDir, platform);
88-
});
87+
for (let k = 0; k < liveSyncProcessInfo.platforms.length; k++) {
88+
await this.$prepareController.stopWatchers(projectDir, liveSyncProcessInfo.platforms[k]);
89+
}
8990

9091
liveSyncProcessInfo.isStopped = true;
9192

lib/definitions/cleanup-service.d.ts

+14
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,18 @@ interface ICleanupService extends IShouldDispose, IDisposable {
5656
* @returns {Promise<void>}
5757
*/
5858
removeCleanupJS(jsCommand: IJSCommand): Promise<void>;
59+
60+
/**
61+
* Adds a kill action for the process
62+
* @param pid the pid of the process to be killed
63+
* @returns {Promise<void>}
64+
*/
65+
addKillProcess(pid: string): Promise<void>;
66+
67+
/**
68+
* Removes the kill action for the process
69+
* @param pid the pid of the process to be killed
70+
* @returns {Promise<void>}
71+
*/
72+
removeKillProcess(pid: string): Promise<void>;
5973
}

lib/definitions/prepare.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ declare global {
2323

2424
interface IPrepareController extends EventEmitter {
2525
prepare(prepareData: IPrepareData): Promise<IPrepareResultData>;
26-
stopWatchers(projectDir: string, platform: string): void;
26+
stopWatchers(projectDir: string, platform: string): Promise<void>;
2727
}
2828

2929
interface IPrepareResultData {

lib/services/cleanup-service.ts

+31
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ export class CleanupService implements ICleanupService {
4545
cleanupProcess.send(<IJSCleanupMessage>{ messageType: CleanupProcessMessage.RemoveJSFileToRequire, jsCommand});
4646
}
4747

48+
public async addKillProcess(pid: string): Promise<void> {
49+
const killSpawnCommandInfo = this.getKillProcesSpawnInfo(pid);
50+
await this.addCleanupCommand(killSpawnCommandInfo);
51+
}
52+
53+
public async removeKillProcess(pid: string): Promise<void> {
54+
const killSpawnCommandInfo = this.getKillProcesSpawnInfo(pid);
55+
await this.removeCleanupCommand(killSpawnCommandInfo);
56+
}
57+
4858
@exported("cleanupService")
4959
public setCleanupLogFile(filePath: string): void {
5060
this.pathToCleanupLogFile = filePath;
@@ -121,6 +131,27 @@ export class CleanupService implements ICleanupService {
121131

122132
return cleanupProcessArgs;
123133
}
134+
135+
private getKillProcesSpawnInfo(pid: string): ISpawnCommandInfo {
136+
let command;
137+
let args;
138+
switch (process.platform) {
139+
case 'win32':
140+
command = "taskkill";
141+
args = ["/pid", pid, "/T", "/F"];
142+
break;
143+
144+
default:
145+
command = path.join(__dirname, '../bash-scripts/terminateProcess.sh');
146+
args = [pid];
147+
break;
148+
}
149+
150+
return {
151+
command,
152+
args
153+
};
154+
}
124155
}
125156

126157
$injector.register("cleanupService", CleanupService);

lib/services/webpack/webpack-compiler-service.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
1414
public $hostInfo: IHostInfo,
1515
private $logger: ILogger,
1616
private $pluginsService: IPluginsService,
17-
private $mobileHelper: Mobile.IMobileHelper
17+
private $mobileHelper: Mobile.IMobileHelper,
18+
private $cleanupService: ICleanupService
1819
) { super(); }
1920

2021
public async compileWithWatch(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise<any> {
@@ -104,11 +105,15 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
104105
});
105106
}
106107

107-
public stopWebpackCompiler(platform: string): void {
108+
public async stopWebpackCompiler(platform: string): Promise<void> {
108109
if (platform) {
109-
this.stopWebpackForPlatform(platform);
110+
await this.stopWebpackForPlatform(platform);
110111
} else {
111-
Object.keys(this.webpackProcesses).forEach(pl => this.stopWebpackForPlatform(pl));
112+
const webpackedPlatforms = Object.keys(this.webpackProcesses);
113+
114+
for (let i = 0; i < webpackedPlatforms.length; i++) {
115+
await this.stopWebpackForPlatform(webpackedPlatforms[i]);
116+
}
112117
}
113118
}
114119

@@ -139,6 +144,7 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
139144
const childProcess = this.$childProcess.spawn("node", args, { cwd: projectData.projectDir, stdio });
140145

141146
this.webpackProcesses[platformData.platformNameLowerCase] = childProcess;
147+
await this.$cleanupService.addKillProcess(childProcess.pid.toString());
142148

143149
return childProcess;
144150
}
@@ -233,9 +239,10 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
233239
};
234240
}
235241

236-
private stopWebpackForPlatform(platform: string) {
242+
private async stopWebpackForPlatform(platform: string) {
237243
this.$logger.trace(`Stopping webpack watch for platform ${platform}.`);
238244
const webpackProcess = this.webpackProcesses[platform];
245+
await this.$cleanupService.removeKillProcess(webpackProcess.pid.toString());
239246
if (webpackProcess) {
240247
webpackProcess.kill("SIGINT");
241248
delete this.webpackProcesses[platform];

lib/services/webpack/webpack.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ declare global {
66
interface IWebpackCompilerService extends EventEmitter {
77
compileWithWatch(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise<any>;
88
compileWithoutWatch(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise<void>;
9-
stopWebpackCompiler(platform: string): void;
9+
stopWebpackCompiler(platform: string): Promise<void>;
1010
}
1111

1212
interface IWebpackEnvOptions {

0 commit comments

Comments
 (0)