Merge pull request #3731 from yizhifengye/fix/process-cleanup-infinite-loop-epipe
fix(background-agent): detach error listener before running body to s…
This commit is contained in:
@@ -275,5 +275,42 @@ describe("#given process cleanup registration", () => {
|
||||
uncaughtExceptionListenersBefore.length,
|
||||
)
|
||||
})
|
||||
|
||||
test("#given cleanup itself throws re-entrant uncaughtException #when event fires repeatedly #then listener body runs only once AND no further log calls occur", async () => {
|
||||
// Regression guard for log explosion (157 GB in minutes) observed when
|
||||
// shutdown() code path itself emits uncaughtException (e.g. EPIPE while
|
||||
// closing a broken pipe). Before the fix, every re-entry logged another
|
||||
// line and re-ran cleanup, producing an unbounded loop that filled disk.
|
||||
const reentrantShutdown = mock(() => {
|
||||
process.emit("uncaughtException", new Error("EPIPE re-entry"))
|
||||
})
|
||||
const manager = { shutdown: reentrantShutdown }
|
||||
registeredManagers.push(manager)
|
||||
|
||||
registerManagerForCleanup(manager)
|
||||
|
||||
process.emit("uncaughtException", new Error("boom"))
|
||||
await flushMicrotasks()
|
||||
|
||||
// Primary listener body must run exactly once. Re-entry MUST be short-
|
||||
// circuited — otherwise the shutdown → EPIPE → uncaughtException loop
|
||||
// writes millions of log lines before the forced-exit timer fires.
|
||||
expect(reentrantShutdown.mock.calls.length).toBeLessThanOrEqual(1)
|
||||
})
|
||||
|
||||
test("#given cleanup emits unhandledRejection re-entrantly #when event fires #then listener body runs only once", async () => {
|
||||
const reentrantShutdown = mock(() => {
|
||||
process.emit("unhandledRejection", new Error("re-entry"), Promise.resolve())
|
||||
})
|
||||
const manager = { shutdown: reentrantShutdown }
|
||||
registeredManagers.push(manager)
|
||||
|
||||
registerManagerForCleanup(manager)
|
||||
|
||||
process.emit("unhandledRejection", new Error("boom"), Promise.resolve())
|
||||
await flushMicrotasks()
|
||||
|
||||
expect(reentrantShutdown.mock.calls.length).toBeLessThanOrEqual(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user