@@ -6,31 +6,33 @@ const {spawn} = require('child_process');
66const split = require ( 'split' ) ;
77const { assert} = require ( 'chai' ) ;
88
9- const log = require ( '../../lib/logger' ) ( { hostname : 'test' , MODULE : 'Process' } ) ;
9+ const log = require ( '../logger' ) ( { MODULE : 'node/Process' } ) ;
10+ const explicitPromise = require ( '../explicitPromise' ) ;
1011
12+ /**
13+ * Launch a system process (requires node.js) and manage its lifecycle and standard streams.
14+ */
1115class Process extends EventEmitter {
12- constructor ( { executablePath, args, env, cwd, enableExtraFd, outputFilter = s => s } ) {
16+ constructor ( { executablePath, args, env, cwd, enableExtraFd, outputFilter = s => s , killTimeout = 10000 } ) {
1317 super ( ) ;
1418 this . executablePath = executablePath ;
1519 this . args = args || [ ] ;
1620 this . env = env || { } ;
1721 this . cwd = cwd || undefined ; // undefined = inherit
1822 this . enableExtraFd = Boolean ( enableExtraFd || false ) ;
1923 this . outputFilter = outputFilter ;
24+ this . killTimeout = killTimeout ;
2025
2126 this . runningChild = null ;
22- this . childrenLifeTime = Promise . resolve ( ) ;
2327 this . lastProcessStart = NaN ;
2428 this . lastProcessExit = NaN ;
2529 this . processExitCount = 0 ;
26-
27- this . _lastStartSymbol = null ;
30+ this . _childLifeTime = Promise . resolve ( ) ;
2831 }
2932
3033 _waitableEmit ( event , ...args ) {
3134 const waits = [ ] ;
3235 this . emit ( event , ...args , p => waits . push ( typeof p === 'function' ? p ( ) : p ) ) ;
33-
3436 return Promise . all ( waits ) ;
3537 }
3638
@@ -51,33 +53,12 @@ class Process extends EventEmitter {
5153 async start ( ) {
5254 assert . isNotOk ( this . runningChild , 'start(): The child process has already been started' ) ;
5355
54- const lastStartSymbol = Symbol ( ) ;
55- this . _lastStartSymbol = lastStartSymbol ;
56-
57- let lifeTimeResolve ;
58- let lifeTimeReject ;
59-
60- const childLifeTime = new Promise ( ( resolve , reject ) => {
61- lifeTimeResolve = resolve ;
62- lifeTimeReject = reject ;
63- } )
64- . finally ( ( ) => {
65- if ( this . _lastStartSymbol === lastStartSymbol ) {
66- this . runningChild = null ;
67- }
68- } ) ;
69-
70- this . childrenLifeTime = this . childrenLifeTime
71- . finally ( async ( ) => {
72- await childLifeTime ;
73-
74- if ( this . _lastStartSymbol === lastStartSymbol ) {
75- await this . _waitableEmit ( 'stopped' ) ;
76- log . info ( 'Process stopped' ) ;
77- this . emit ( 'afterStopped' ) ;
78- }
79-
80- return null ;
56+ const [ lifeTimePromise , lifeTimeResolve ] = explicitPromise ( ) ;
57+ this . _childLifeTime = lifeTimePromise . then ( async ( reason ) => {
58+ this . runningChild = null ;
59+ await this . _waitableEmit ( 'stopped' , reason ) ;
60+ log . info ( 'Process stopped' ) ;
61+ this . emit ( 'afterStopped' , reason ) ;
8162 } ) ;
8263
8364 // set this value right away to make sure that we never create duplicate processes
@@ -125,14 +106,14 @@ class Process extends EventEmitter {
125106 log . info ( { code, executablePath, signal} , 'Process exit' ) ;
126107 this . lastProcessExit = Date . now ( ) ;
127108 ++ this . processExitCount ;
128- lifeTimeResolve ( { code, signal} ) ;
109+ lifeTimeResolve ( Object . freeze ( { error : null , code, signal} ) ) ;
129110 } ) ;
130111
131112 childProcess . on ( 'error' , err => {
132113 // The process could not be spawned, or The process could not be killed, or Sending a message to the child process
133114 // failed.
134115 log . error ( { childPid, err, executablePath} , 'Error spawning process' ) ;
135- lifeTimeReject ( err ) ;
116+ lifeTimeResolve ( Object . freeze ( { error : err } ) ) ;
136117 } ) ;
137118
138119 childProcess . stdout . pipe ( split ( ) ) . on ( 'data' , line => {
@@ -166,24 +147,21 @@ class Process extends EventEmitter {
166147 this . emit ( 'FD3' , line ) ;
167148 } ) ;
168149 }
150+
151+ await this . _waitableEmit ( 'afterStart' ) ;
169152 }
170153 catch ( err ) {
171- lifeTimeReject ( err ) ;
154+ lifeTimeResolve ( Object . freeze ( { error : err } ) ) ;
172155 throw err ;
173156 }
174-
175- // try to throw if the child immediately fails to start:
176- await Promise . race ( [ Promise . delay ( 10 ) , childLifeTime ] ) ;
177- await this . _waitableEmit ( 'afterStart' ) ;
178157 }
179158
180159 async stop ( ) {
181160 if ( ! this . runningChild ||
182161 this . runningChild . stopping ||
183162 ! this . runningChild . childProcess ) {
184163 // already stopping or there was an error creating the childProcess
185- await this . childrenLifeTime . catch ( ( ) => { } ) ;
186-
164+ await this . waitForChildStop ( ) ;
187165 return ;
188166 }
189167
@@ -199,11 +177,15 @@ class Process extends EventEmitter {
199177 // SIGKILL = forceful termination
200178 log . info ( { signal : 'SIGKILL' } , 'Stopping process' ) ;
201179 childProcess . kill ( 'SIGKILL' ) ;
202- } , 10000 ) ;
180+ } , this . killTimeout ) ;
203181
204- await this . childrenLifeTime . catch ( ( ) => { } ) ;
182+ await this . waitForChildStop ( ) ;
205183 clearTimeout ( killTimer ) ;
206184 }
185+
186+ async waitForChildStop ( ) {
187+ await this . _childLifeTime ;
188+ }
207189}
208190
209191module . exports = Process ;
0 commit comments