Skip to content

Commit 74910ff

Browse files
authored
Hotswap on SIGUSR1 (#1970)
1 parent 3c90b1e commit 74910ff

File tree

1 file changed

+33
-16
lines changed

1 file changed

+33
-16
lines changed

src/node/wrapper.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ export class IpcMain {
3232
public readonly onMessage = this._onMessage.event
3333
private readonly _onDispose = new Emitter<NodeJS.Signals | undefined>()
3434
public readonly onDispose = this._onDispose.event
35-
public readonly exit: (code?: number) => never
35+
public readonly processExit: (code?: number) => never
3636

3737
public constructor(public readonly parentPid?: number) {
3838
process.on("SIGINT", () => this._onDispose.emit("SIGINT"))
3939
process.on("SIGTERM", () => this._onDispose.emit("SIGTERM"))
4040
process.on("exit", () => this._onDispose.emit(undefined))
4141

4242
// Ensure we control when the process exits.
43-
this.exit = process.exit
43+
this.processExit = process.exit
4444
process.exit = function (code?: number) {
4545
logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`)
4646
} as (code?: number) => never
@@ -71,6 +71,14 @@ export class IpcMain {
7171
}
7272
}
7373

74+
public exit(error?: number | ProcessError): never {
75+
if (error && typeof error !== "number") {
76+
this.processExit(typeof error.code === "number" ? error.code : 1)
77+
} else {
78+
this.processExit(error)
79+
}
80+
}
81+
7482
public handshake(child?: cp.ChildProcess): Promise<void> {
7583
return new Promise((resolve, reject) => {
7684
const target = child || process
@@ -161,28 +169,37 @@ export class WrapperProcess {
161169
}
162170
})
163171

164-
ipcMain().onMessage(async (message) => {
172+
ipcMain().onMessage((message) => {
165173
switch (message.type) {
166174
case "relaunch":
167175
logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`)
168176
this.currentVersion = message.version
169-
this.started = undefined
170-
if (this.process) {
171-
this.process.removeAllListeners()
172-
this.process.kill()
173-
}
174-
try {
175-
await this.start()
176-
} catch (error) {
177-
logger.error(error.message)
178-
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
179-
}
177+
this.relaunch()
180178
break
181179
default:
182180
logger.error(`Unrecognized message ${message}`)
183181
break
184182
}
185183
})
184+
185+
process.on("SIGUSR1", async () => {
186+
logger.info("Received SIGUSR1; hotswapping")
187+
this.relaunch()
188+
})
189+
}
190+
191+
private async relaunch(): Promise<void> {
192+
this.started = undefined
193+
if (this.process) {
194+
this.process.removeAllListeners()
195+
this.process.kill()
196+
}
197+
try {
198+
await this.start()
199+
} catch (error) {
200+
logger.error(error.message)
201+
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
202+
}
186203
}
187204

188205
public start(): Promise<void> {
@@ -244,13 +261,13 @@ export const wrap = (fn: () => Promise<void>): void => {
244261
.then(() => fn())
245262
.catch((error: ProcessError): void => {
246263
logger.error(error.message)
247-
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
264+
ipcMain().exit(error)
248265
})
249266
} else {
250267
const wrapper = new WrapperProcess(require("../../package.json").version)
251268
wrapper.start().catch((error) => {
252269
logger.error(error.message)
253-
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
270+
ipcMain().exit(error)
254271
})
255272
}
256273
}

0 commit comments

Comments
 (0)