@@ -5,6 +5,59 @@ import * as rfs from "rotating-file-stream"
5
5
import { Emitter } from "../common/emitter"
6
6
import { paths } from "./util"
7
7
8
+ const timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
9
+
10
+ /**
11
+ * Listen to a single message from a process. Reject if the process errors,
12
+ * exits, or times out.
13
+ *
14
+ * `fn` is a function that determines whether the message is the one we're
15
+ * waiting for.
16
+ */
17
+ export function onMessage < M , T extends M > (
18
+ proc : cp . ChildProcess | NodeJS . Process ,
19
+ fn : ( message : M ) => message is T ,
20
+ customLogger ?: Logger ,
21
+ ) : Promise < T > {
22
+ return new Promise ( ( resolve , reject ) => {
23
+ const cleanup = ( ) => {
24
+ proc . off ( "error" , onError )
25
+ proc . off ( "exit" , onExit )
26
+ proc . off ( "message" , onMessage )
27
+ clearTimeout ( timeout )
28
+ }
29
+
30
+ const timeout = setTimeout ( ( ) => {
31
+ cleanup ( )
32
+ reject ( new Error ( "timed out" ) )
33
+ } , timeoutInterval )
34
+
35
+ const onError = ( error : Error ) => {
36
+ cleanup ( )
37
+ reject ( error )
38
+ }
39
+
40
+ const onExit = ( code : number ) => {
41
+ cleanup ( )
42
+ reject ( new Error ( `exited unexpectedly with code ${ code } ` ) )
43
+ }
44
+
45
+ const onMessage = ( message : M ) => {
46
+ ; ( customLogger || logger ) . trace ( "got message" , field ( "message" , message ) )
47
+ if ( fn ( message ) ) {
48
+ cleanup ( )
49
+ resolve ( message )
50
+ }
51
+ }
52
+
53
+ proc . on ( "message" , onMessage )
54
+ // NodeJS.Process doesn't have `error` but binding anyway shouldn't break
55
+ // anything. It does have `exit` but the types aren't working.
56
+ ; ( proc as cp . ChildProcess ) . on ( "error" , onError )
57
+ ; ( proc as cp . ChildProcess ) . on ( "exit" , onExit )
58
+ } )
59
+ }
60
+
8
61
interface HandshakeMessage {
9
62
type : "handshake"
10
63
}
@@ -111,19 +164,15 @@ class ChildProcess extends Process {
111
164
/**
112
165
* Initiate the handshake and wait for a response from the parent.
113
166
*/
114
- public handshake ( ) : Promise < void > {
115
- return new Promise ( ( resolve ) => {
116
- const onMessage = ( message : Message ) : void => {
117
- logger . debug ( `received message from ${ this . parentPid } ` , field ( "message" , message ) )
118
- if ( message . type === "handshake" ) {
119
- process . removeListener ( "message" , onMessage )
120
- resolve ( )
121
- }
122
- }
123
- // Initiate the handshake and wait for the reply.
124
- process . on ( "message" , onMessage )
125
- this . send ( { type : "handshake" } )
126
- } )
167
+ public async handshake ( ) : Promise < void > {
168
+ this . send ( { type : "handshake" } )
169
+ await onMessage < Message , HandshakeMessage > (
170
+ process ,
171
+ ( message ) : message is HandshakeMessage => {
172
+ return message . type === "handshake"
173
+ } ,
174
+ this . logger ,
175
+ )
127
176
}
128
177
129
178
/**
@@ -270,23 +319,15 @@ export class ParentProcess extends Process {
270
319
/**
271
320
* Wait for a handshake from the child then reply.
272
321
*/
273
- private handshake ( child : cp . ChildProcess ) : Promise < void > {
274
- return new Promise ( ( resolve , reject ) => {
275
- const onMessage = ( message : Message ) : void => {
276
- logger . debug ( `received message from ${ child . pid } ` , field ( "message" , message ) )
277
- if ( message . type === "handshake" ) {
278
- child . removeListener ( "message" , onMessage )
279
- child . on ( "message" , ( msg ) => this . _onChildMessage . emit ( msg ) )
280
- child . send ( { type : "handshake" } )
281
- resolve ( )
282
- }
283
- }
284
- child . on ( "message" , onMessage )
285
- child . once ( "error" , reject )
286
- child . once ( "exit" , ( code ) => {
287
- reject ( new ProcessError ( `Unexpected exit with code ${ code } ` , code !== null ? code : undefined ) )
288
- } )
289
- } )
322
+ private async handshake ( child : cp . ChildProcess ) : Promise < void > {
323
+ await onMessage < Message , HandshakeMessage > (
324
+ child ,
325
+ ( message ) : message is HandshakeMessage => {
326
+ return message . type === "handshake"
327
+ } ,
328
+ this . logger ,
329
+ )
330
+ child . send ( { type : "handshake" } )
290
331
}
291
332
}
292
333
0 commit comments