Skip to content

Commit c8e99cb

Browse files
committed
fix: handle signals to avoid orphaned processes
1 parent 3d6fb29 commit c8e99cb

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

packages/opencode/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { EOL } from "os"
2626
import { WebCommand } from "./cli/cmd/web"
2727
import { PrCommand } from "./cli/cmd/pr"
2828
import { SessionCommand } from "./cli/cmd/session"
29+
import { registerSignalHandlers } from "./signal"
2930

3031
process.on("unhandledRejection", (e) => {
3132
Log.Default.error("rejection", {
@@ -39,6 +40,8 @@ process.on("uncaughtException", (e) => {
3940
})
4041
})
4142

43+
registerSignalHandlers()
44+
4245
const cli = yargs(hideBin(process.argv))
4346
.parserConfiguration({ "populate--": true })
4447
.scriptName("opencode")

packages/opencode/src/signal.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Instance } from "./project/instance"
2+
import { Log } from "./util/log"
3+
4+
const SHUTDOWN_TIMEOUT_MS = 5000
5+
6+
const SIGNAL_EXIT_CODES: Record<string, number> = {
7+
SIGTERM: 128 + 15,
8+
SIGINT: 128 + 2,
9+
SIGHUP: 128 + 1,
10+
}
11+
12+
let shuttingDown = false
13+
14+
async function gracefulShutdown(signal: string) {
15+
if (shuttingDown) return
16+
shuttingDown = true
17+
18+
const log = Log.create({ service: "signal" })
19+
log.info("received signal, shutting down gracefully", { signal })
20+
21+
const timeout = setTimeout(() => {
22+
log.warn("shutdown timeout, forcing exit", {
23+
timeoutMs: SHUTDOWN_TIMEOUT_MS,
24+
signal,
25+
})
26+
process.exit(SIGNAL_EXIT_CODES[signal] ?? 1)
27+
}, SHUTDOWN_TIMEOUT_MS)
28+
29+
try {
30+
await Instance.disposeAll()
31+
} catch (error) {
32+
log.error("error during shutdown", { error })
33+
} finally {
34+
clearTimeout(timeout)
35+
process.exit(SIGNAL_EXIT_CODES[signal] ?? 0)
36+
}
37+
}
38+
39+
export function registerSignalHandlers() {
40+
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
41+
process.on(signal, () => {
42+
void gracefulShutdown(signal)
43+
})
44+
}
45+
}

0 commit comments

Comments
 (0)