38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
export interface ClosableRuntime {
|
|
close(): Promise<void>;
|
|
}
|
|
export interface SignalTarget {
|
|
once(event: 'SIGINT' | 'SIGTERM', listener: () => void): unknown;
|
|
removeListener(event: 'SIGINT' | 'SIGTERM', listener: () => void): unknown;
|
|
}
|
|
export function installBoundedShutdown(
|
|
target: SignalTarget,
|
|
runtime: ClosableRuntime,
|
|
options: { readonly timeoutMs?: number; readonly terminate?: () => void } = {},
|
|
): () => Promise<void> {
|
|
let shutdown: Promise<void> | undefined;
|
|
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
const stop = (): Promise<void> => {
|
|
if (shutdown) return shutdown;
|
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
const deadline = new Promise<void>((resolve) => {
|
|
timeout = setTimeout(() => {
|
|
options.terminate?.();
|
|
resolve();
|
|
}, timeoutMs);
|
|
});
|
|
shutdown = Promise.race([runtime.close(), deadline]).finally(() => {
|
|
if (timeout !== undefined) clearTimeout(timeout);
|
|
target.removeListener('SIGINT', listener);
|
|
target.removeListener('SIGTERM', listener);
|
|
});
|
|
return shutdown;
|
|
};
|
|
const listener = (): void => {
|
|
void stop();
|
|
};
|
|
target.once('SIGINT', listener);
|
|
target.once('SIGTERM', listener);
|
|
return stop;
|
|
}
|