Hotswap on SIGUSR1 (#1970)

This commit is contained in:
Asher 2020-08-17 14:17:55 -05:00 committed by GitHub
parent 3c90b1e327
commit 74910ffcdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 33 additions and 16 deletions

View File

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