Hotswap on SIGUSR1

This commit is contained in:
Asher 2020-08-06 13:14:24 -05:00
parent 7d02f34f71
commit 8cbc753cbb
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A

View File

@ -32,7 +32,7 @@ export class IpcMain {
public readonly onMessage = this._onMessage.event
private readonly _onDispose = new Emitter<NodeJS.Signals | undefined>()
public readonly onDispose = this._onDispose.event
public readonly exit: (code?: number) => never
public readonly processExit: (code?: number) => never
public constructor(public readonly parentPid?: number) {
process.on("SIGINT", () => this._onDispose.emit("SIGINT"))
@ -40,7 +40,7 @@ export class IpcMain {
process.on("exit", () => this._onDispose.emit(undefined))
// Ensure we control when the process exits.
this.exit = process.exit
this.processExit = process.exit
process.exit = function (code?: number) {
logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`)
} as (code?: number) => never
@ -71,6 +71,14 @@ export class IpcMain {
}
}
public exit(error?: number | ProcessError): never {
if (error && typeof error !== "number") {
this.processExit(typeof error.code === "number" ? error.code : 1)
} else {
this.processExit(error)
}
}
public handshake(child?: cp.ChildProcess): Promise<void> {
return new Promise((resolve, reject) => {
const target = child || process
@ -161,28 +169,37 @@ export class WrapperProcess {
}
})
ipcMain().onMessage(async (message) => {
ipcMain().onMessage((message) => {
switch (message.type) {
case "relaunch":
logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`)
this.currentVersion = message.version
this.started = undefined
if (this.process) {
this.process.removeAllListeners()
this.process.kill()
}
try {
await this.start()
} catch (error) {
logger.error(error.message)
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
}
this.relaunch()
break
default:
logger.error(`Unrecognized message ${message}`)
break
}
})
process.on("SIGUSR1", async () => {
logger.info("Received SIGUSR1; hotswapping")
this.relaunch()
})
}
private async relaunch(): Promise<void> {
this.started = undefined
if (this.process) {
this.process.removeAllListeners()
this.process.kill()
}
try {
await this.start()
} catch (error) {
logger.error(error.message)
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
}
}
public start(): Promise<void> {
@ -244,13 +261,13 @@ export const wrap = (fn: () => Promise<void>): void => {
.then(() => fn())
.catch((error: ProcessError): void => {
logger.error(error.message)
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
ipcMain().exit(error)
})
} else {
const wrapper = new WrapperProcess(require("../../package.json").version)
wrapper.start().catch((error) => {
logger.error(error.message)
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
ipcMain().exit(error)
})
}
}