@@ -13,26 +13,54 @@ const key = 'pid';
1313const millis = 10000 ;
1414const none = - 1 ;
1515const prefix = 'Trace Server' ;
16- const suffix = ' failure or so .' ;
16+ const suffix = ' failure.' ;
1717const SHUTDOWN_DELAY = 2000 ;
1818
1919export class TraceServer {
2020 private server : ChildProcess | undefined ;
21+ private errorString : string | undefined ;
22+ private stderr : string | undefined ;
23+ private context : vscode . ExtensionContext | undefined ;
24+
25+ private handleStartUpError = ( code : number | null ) : void => {
26+ this . errorString = 'Code: ' + code + ( this . stderr !== undefined ? '. Error message: ' + this . stderr : '.' ) ;
27+ } ;
28+
29+ private handleStartUpStderr = ( data : string | undefined ) : void => {
30+ this . stderr = data ;
31+ } ;
32+
33+ private handleError = ( code : number | null ) : void => {
34+ if ( this . context ) {
35+ this . context ?. workspaceState . update ( key , none ) ;
36+ }
37+ this . server = undefined ;
38+ this . setStatusIfAvailable ( false ) ;
39+ vscode . window . showErrorMessage (
40+ prefix + ' exited unexpectedly' + ( code !== null ? ' with code ' + code : '' ) + '!'
41+ ) ;
42+ } ;
2143
2244 private async start ( context : vscode . ExtensionContext | undefined ) {
2345 const from = this . getSettings ( ) ;
2446 const server = spawn ( this . getPath ( from ) , this . getArgs ( from ) ) ;
2547
2648 if ( ! server . pid ) {
27- this . showError ( prefix + ' startup' + suffix ) ;
49+ // Failed to spawn the child process due to errors, PID is undefined and an error is emitted.
50+ await new Promise < void > ( resolve => {
51+ server . once ( 'error' , error => {
52+ this . showError ( prefix + ' startup' + suffix + ': ' + error ) ;
53+ } ) ;
54+ resolve ( ) ;
55+ } ) ;
2856 return ;
2957 }
3058 this . server = server ;
3159 await context ?. workspaceState . update ( key , this . server . pid ) ;
3260 await this . waitFor ( context ) ;
3361 }
3462
35- async stopOrReset ( context : vscode . ExtensionContext | undefined ) {
63+ async stopOrReset ( context : vscode . ExtensionContext | undefined , reportNotStopped : boolean = true ) {
3664 const pid : number | undefined = context ?. workspaceState . get ( key ) ;
3765 const not = prefix + ' not stopped as none running or owned by us.' ;
3866 if ( pid === none ) {
@@ -43,6 +71,8 @@ export class TraceServer {
4371 let id : NodeJS . Timeout ;
4472 // recovering from workspaceState => no this.server set
4573 if ( this . server ) {
74+ this . server . off ( 'exit' , this . handleError ) ;
75+ this . context = undefined ;
4676 this . server . once ( 'exit' , ( ) => {
4777 this . showStatus ( false ) ;
4878 clearTimeout ( id ) ;
@@ -66,7 +96,7 @@ export class TraceServer {
6696 } ) ;
6797 }
6898 ) ;
69- } else {
99+ } else if ( reportNotStopped ) {
70100 vscode . window . showWarningMessage ( not ) ;
71101 }
72102 await context ?. workspaceState . update ( key , none ) ;
@@ -81,6 +111,9 @@ export class TraceServer {
81111 return ;
82112 }
83113 if ( pid ) {
114+ // Remove all existing listeners to avoid unnecessary pop-ups
115+ this . server ?. off ( 'exit' , this . handleError ) ;
116+ this . context = undefined ;
84117 treeKill ( pid ) ;
85118 // Allow the treeKill to finish collecting and killing all
86119 // spawned processes.
@@ -160,6 +193,12 @@ export class TraceServer {
160193 }
161194
162195 private async waitFor ( context : vscode . ExtensionContext | undefined ) {
196+ if ( this . server === undefined ) {
197+ return ;
198+ }
199+
200+ this . context = context ;
201+ const server = this . server ;
163202 await vscode . window . withProgress (
164203 {
165204 location : vscode . ProgressLocation . Notification ,
@@ -168,27 +207,52 @@ export class TraceServer {
168207 } ,
169208 async progress => {
170209 progress . report ( { message : 'starting up...' } ) ;
171- let timeout = false ;
172- const timeoutId = setTimeout ( ( ) => ( timeout = true ) , millis ) ;
173-
174- // eslint-disable-next-line no-constant-condition
175- while ( true ) {
176- if ( await this . isUp ( ) ) {
177- this . showStatus ( true ) ;
178- clearTimeout ( timeoutId ) ;
179- break ;
180- }
181- if ( timeout ) {
182- this . showError ( prefix + ' startup timed-out after ' + millis + 'ms.' ) ;
183- await this . stopOrReset ( context ) ;
184- break ;
210+ try {
211+ let timeout = false ;
212+ const timeoutId = setTimeout ( ( ) => ( timeout = true ) , millis ) ;
213+
214+ server . stderr ?. once ( 'data' , this . handleStartUpStderr ) ;
215+ server . once ( 'exit' , this . handleStartUpError ) ;
216+
217+ // eslint-disable-next-line no-constant-condition
218+ while ( true ) {
219+ if ( await this . isUp ( ) ) {
220+ clearTimeout ( timeoutId ) ;
221+ this . showStatus ( true ) ;
222+ break ;
223+ }
224+
225+ // Check if trace server exited with error
226+ if ( this . errorString !== undefined ) {
227+ const errorString = this . errorString ;
228+ this . errorString = undefined ;
229+ throw new Error ( errorString ) ;
230+ }
231+
232+ // Check for overall timeout
233+ if ( timeout ) {
234+ await this . stopOrReset ( context , false ) ;
235+ throw new Error ( prefix + ' startup timed-out after ' + millis + 'ms.' ) ;
236+ }
237+
238+ // Wait before trying again
239+ await new Promise < void > ( resolve => setTimeout ( ( ) => resolve ( ) , 1000 ) ) ;
185240 }
241+ } catch ( error ) {
242+ this . showErrorDetailed ( prefix + ' starting up' + suffix , error as Error ) ;
243+ return ;
244+ } finally {
245+ // Remove all listener needed during startup
246+ server . off ( 'exit' , this . handleStartUpError ) ;
247+ server . stderr ?. off ( 'data' , this . handleStartUpStderr ) ;
248+ // Add listener to catch when server exits unexpectedly
249+ server . once ( 'exit' , this . handleError ) ;
186250 }
187251 }
188252 ) ;
189253 }
190254
191- private async isUp ( ) {
255+ private async isUp ( ) : Promise < boolean > {
192256 const client = new TspClient ( this . getServerUrl ( ) ) ;
193257 const health = await client . checkHealth ( ) ;
194258 const status = health . getModel ( ) ?. status ;
@@ -198,18 +262,15 @@ export class TraceServer {
198262 private async showError ( message : string ) {
199263 console . error ( message ) ;
200264 vscode . window . showErrorMessage ( message ) ;
201- const disclaimer = ' running, despite this error.' ;
202265 const up = await this . isUp ( ) ;
203266 if ( up ) {
204- vscode . window . showWarningMessage ( prefix + ' is still' + disclaimer ) ;
205- } else {
206- vscode . window . showWarningMessage ( prefix + ' is not' + disclaimer ) ;
267+ vscode . window . showWarningMessage ( prefix + ' is still running, despite this error.' ) ;
207268 }
208269 this . setStatusIfAvailable ( up ) ;
209270 }
210271
211272 private showErrorDetailed ( message : string , error : Error ) {
212- const details = error . name + ' - ' + error . message ;
273+ const details = error . name + ' - ' + error ? .message ;
213274 vscode . window . showErrorMessage ( details ) ;
214275 console . error ( details ) ;
215276 this . showError ( message ) ;
0 commit comments