@@ -20,6 +20,7 @@ export interface Locator {
2020 version : string ;
2121 // Ways of reaching the hub as URL prefix, such as http://127.0.0.1:4000
2222 origins : string [ ] ;
23+ pid : number ;
2324}
2425
2526export interface EmulatorHubArgs {
@@ -52,13 +53,7 @@ export class EmulatorHub extends ExpressBasedEmulator {
5253
5354 const data = fs . readFileSync ( locatorPath , "utf8" ) . toString ( ) ;
5455 const locator = JSON . parse ( data ) as Locator ;
55-
56- if ( locator . version !== this . CLI_VERSION ) {
57- logger . debug (
58- `Found emulator locator with different version: ${ JSON . stringify ( locator ) } , CLI_VERSION: ${ this . CLI_VERSION } ` ,
59- ) ;
60- }
61-
56+ logger . debug ( `Found emulator hub locator: ${ JSON . stringify ( locator ) } ` ) ;
6257 return locator ;
6358 }
6459
@@ -69,7 +64,6 @@ export class EmulatorHub extends ExpressBasedEmulator {
6964 }
7065 const filename = `hub-${ projectId } .json` ;
7166 const locatorPath = path . join ( dir , filename ) ;
72- logger . debug ( `Emulator locator file path: ${ locatorPath } ` ) ;
7367 return locatorPath ;
7468 }
7569
@@ -99,7 +93,7 @@ export class EmulatorHub extends ExpressBasedEmulator {
9993 const app = await super . createExpressApp ( ) ;
10094 app . get ( "/" , ( req , res ) => {
10195 res . json ( {
102- ...this . getLocator ( ) ,
96+ ...this . buildLocator ( ) ,
10397 // For backward compatibility:
10498 host : utils . connectableHostname ( this . args . listen [ 0 ] . address ) ,
10599 port : this . args . listen [ 0 ] . port ,
@@ -194,14 +188,13 @@ export class EmulatorHub extends ExpressBasedEmulator {
194188
195189 async stop ( ) : Promise < void > {
196190 await super . stop ( ) ;
197- await this . deleteLocatorFile ( ) ;
198191 }
199192
200193 getName ( ) : Emulators {
201194 return Emulators . HUB ;
202195 }
203196
204- private getLocator ( ) : Locator {
197+ private buildLocator ( ) : Locator {
205198 const version = pkg . version ;
206199 const origins : string [ ] = [ ] ;
207200 for ( const spec of this . args . listen ) {
@@ -214,44 +207,51 @@ export class EmulatorHub extends ExpressBasedEmulator {
214207 return {
215208 version,
216209 origins,
210+ pid : process . pid ,
217211 } ;
218212 }
219213
220214 private async writeLocatorFile ( ) : Promise < void > {
221215 const projectId = this . args . projectId ;
222- const locatorPath = EmulatorHub . getLocatorFilePath ( projectId ) ;
223- const locator = this . getLocator ( ) ;
224-
225- if ( fs . existsSync ( locatorPath ) ) {
216+ const prevLocator = EmulatorHub . readLocatorFile ( projectId ) ;
217+ if ( prevLocator && prevLocator . pid && isProcessLive ( prevLocator . pid ) ) {
226218 utils . logLabeledWarning (
227219 "emulators" ,
228220 `It seems that you are running multiple instances of the emulator suite for project ${ projectId } . This may result in unexpected behavior.` ,
229221 ) ;
222+ return ;
230223 }
231224
232- logger . debug ( `[hub] writing locator at ${ locatorPath } ` ) ;
233- return new Promise ( ( resolve , reject ) => {
234- fs . writeFile ( locatorPath , JSON . stringify ( locator ) , ( e ) => {
235- if ( e ) {
236- reject ( e ) ;
237- } else {
238- resolve ( ) ;
225+ const locatorPath = EmulatorHub . getLocatorFilePath ( projectId ) ;
226+ logger . debug ( `Write emulator hub locator at ${ locatorPath } ` ) ;
227+ fs . writeFileSync ( locatorPath , JSON . stringify ( this . buildLocator ( ) ) ) ;
228+
229+ // Delete the emulator hub locator file on exit
230+ const cleanup = ( ) => {
231+ try {
232+ const curLocator = EmulatorHub . readLocatorFile ( projectId ) ;
233+ if ( curLocator && curLocator . pid === process . pid ) {
234+ fs . unlinkSync ( locatorPath ) ;
235+ logger . debug ( `Delete emulator hub locator file: ${ locatorPath } ` ) ;
239236 }
240- } ) ;
241- } ) ;
237+ } catch ( e : any ) {
238+ logger . debug ( `Cannot delete emulator hub locator file` , e ) ;
239+ }
240+ } ;
241+ process . on ( "SIGINT" , cleanup ) ;
242+ process . on ( "SIGTERM" , cleanup ) ;
243+ process . on ( "exit" , cleanup ) ;
242244 }
245+ }
243246
244- private async deleteLocatorFile ( ) : Promise < void > {
245- const locatorPath = EmulatorHub . getLocatorFilePath ( this . args . projectId ) ;
246- return new Promise ( ( resolve , reject ) => {
247- fs . unlink ( locatorPath , ( e ) => {
248- // If the file is already deleted, no need to throw.
249- if ( e && e . code !== "ENOENT" ) {
250- reject ( e ) ;
251- } else {
252- resolve ( ) ;
253- }
254- } ) ;
255- } ) ;
247+ function isProcessLive ( pid : number ) : boolean {
248+ try {
249+ // Send signal 0 to check if process is alive.
250+ process . kill ( pid , 0 ) ;
251+ return true ;
252+ } catch ( error : any ) {
253+ // ESRCH: The process does not exist (it's dead)
254+ // EPERM: The process exists, but you don't have permission to signal it (it's live)
255+ return error . code === "EPERM" ;
256256 }
257257}
0 commit comments