diff --git a/src/common/emitter.ts b/src/common/emitter.ts index 7a1ebf668..353ce851e 100644 --- a/src/common/emitter.ts +++ b/src/common/emitter.ts @@ -1,4 +1,10 @@ -import { Callback } from "./types" +import { logger } from "@coder/logger" + +/** + * Event emitter callback. Called with the emitted value and a promise that + * resolves when all emitters have finished. + */ +export type Callback> = (t: T, p: Promise) => R export interface Disposable { dispose(): void @@ -32,8 +38,21 @@ export class Emitter { /** * Emit an event with a value. */ - public emit(value: T): void { - this.listeners.forEach((cb) => cb(value)) + public async emit(value: T): Promise { + let resolve: () => void + const promise = new Promise((r) => (resolve = r)) + + await Promise.all( + this.listeners.map(async (cb) => { + try { + await cb(value, promise) + } catch (error) { + logger.error(error.message) + } + }), + ) + + resolve!() } public dispose(): void { diff --git a/src/common/types.ts b/src/common/types.ts deleted file mode 100644 index a8a0e4c1b..000000000 --- a/src/common/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type Callback = (t: T) => R diff --git a/src/node/wrapper.ts b/src/node/wrapper.ts index cce841901..2e8c51cd0 100644 --- a/src/node/wrapper.ts +++ b/src/node/wrapper.ts @@ -39,13 +39,14 @@ export class IpcMain { process.on("SIGTERM", () => this._onDispose.emit("SIGTERM")) process.on("exit", () => this._onDispose.emit(undefined)) - this.onDispose((signal) => { + this.onDispose((signal, wait) => { // Remove listeners to avoid possibly triggering disposal again. process.removeAllListeners() - // Let any other handlers run first then exit. + // Try waiting for other handlers run first then exit. logger.debug(`${parentPid ? "inner process" : "wrapper"} ${process.pid} disposing`, field("code", signal)) - setTimeout(() => this.exit(0), 0) + wait.then(() => this.exit(0)) + setTimeout(() => this.exit(0), 5000) }) // Kill the inner process if the parent dies. This is for the case where the