-
Notifications
You must be signed in to change notification settings - Fork 440
/
Copy pathchild-pool.ts
118 lines (91 loc) · 2.87 KB
/
child-pool.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { ChildProcess, fork } from 'child_process';
import * as path from 'path';
import { forEach, values, flatten } from 'lodash';
import * as getPort from 'get-port';
import * as fs from 'fs';
import { promisify } from 'util';
const stat = promisify(fs.stat);
export interface ChildProcessExt extends ChildProcess {
processFile?: string;
}
const convertExecArgv = async (execArgv: string[]): Promise<string[]> => {
const standard: string[] = [];
const convertedArgs: string[] = [];
forEach(execArgv, async arg => {
if (arg.indexOf('--inspect') === -1) {
standard.push(arg);
} else {
const argName = arg.split('=')[0];
const port = await getPort();
convertedArgs.push(`${argName}=${port}`);
}
});
return standard.concat(convertedArgs);
};
const initChild = function(child: ChildProcess, processFile: string) {
return new Promise(resolve => {
child.send({ cmd: 'init', value: processFile }, resolve);
});
};
export class ChildPool {
retained: { [key: number]: ChildProcessExt } = {};
free: { [key: string]: ChildProcessExt[] } = {};
constructor() {}
async retain(processFile: string): Promise<ChildProcessExt> {
const _this = this;
let child = _this.getFree(processFile).pop();
if (child) {
_this.retained[child.pid] = child;
return child;
}
const execArgv = await convertExecArgv(process.execArgv);
let masterFile = path.join(__dirname, './master.js');
try {
await stat(masterFile); // would throw if file not exists
} catch (_) {
try {
masterFile = path.join(process.cwd(), 'dist/classes/master.js');
await stat(masterFile);
} finally {
}
}
child = fork(masterFile, [], { execArgv });
child.processFile = processFile;
_this.retained[child.pid] = child;
child.on('exit', _this.remove.bind(_this, child));
await initChild(child, child.processFile);
return child;
}
release(child: ChildProcessExt) {
delete this.retained[child.pid];
this.getFree(child.processFile).push(child);
}
remove(child: ChildProcessExt) {
delete this.retained[child.pid];
const free = this.getFree(child.processFile);
const childIndex = free.indexOf(child);
if (childIndex > -1) {
free.splice(childIndex, 1);
}
}
kill(child: ChildProcess, signal?: string) {
child.kill(signal || 'SIGKILL');
this.remove(child);
}
clean() {
const children = values(this.retained).concat(this.getAllFree());
children.forEach(child => {
// TODO: We may want to use SIGKILL if the process does not die after some time.
this.kill(child, 'SIGTERM');
});
this.retained = {};
this.free = {};
}
getFree(id: string): ChildProcessExt[] {
return (this.free[id] = this.free[id] || []);
}
getAllFree() {
return flatten(values(this.free));
}
}
export const pool = new ChildPool();