From 4f3ef07455c81de9ad264e060cbbbee917d5decb Mon Sep 17 00:00:00 2001 From: Vadzim Date: Fri, 5 Jul 2024 01:20:59 +0200 Subject: [PATCH] Fix crash on aborted timer (#12348) --- src/js/node/timers.promises.ts | 14 +++-- .../timers.promises/timers.promises.test.ts | 52 +++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 test/js/node/timers.promises/timers.promises.test.ts diff --git a/src/js/node/timers.promises.ts b/src/js/node/timers.promises.ts index eb171941a5..e9b8a290af 100644 --- a/src/js/node/timers.promises.ts +++ b/src/js/node/timers.promises.ts @@ -84,10 +84,9 @@ function setTimeoutPromise(after = 1, value, options = {}) { signal.addEventListener("abort", onCancel); } }); - if (typeof onCancel !== "undefined") { - returnValue.finally(() => signal.removeEventListener("abort", onCancel)); - } - return returnValue; + return typeof onCancel !== "undefined" + ? returnValue.finally(() => signal.removeEventListener("abort", onCancel)) + : returnValue; } function setImmediatePromise(value, options = {}) { @@ -124,10 +123,9 @@ function setImmediatePromise(value, options = {}) { signal.addEventListener("abort", onCancel); } }); - if (typeof onCancel !== "undefined") { - returnValue.finally(() => signal.removeEventListener("abort", onCancel)); - } - return returnValue; + return typeof onCancel !== "undefined" + ? returnValue.finally(() => signal.removeEventListener("abort", onCancel)) + : returnValue; } function setIntervalPromise(after = 1, value, options = {}) { diff --git a/test/js/node/timers.promises/timers.promises.test.ts b/test/js/node/timers.promises/timers.promises.test.ts new file mode 100644 index 0000000000..7550c94864 --- /dev/null +++ b/test/js/node/timers.promises/timers.promises.test.ts @@ -0,0 +1,52 @@ +import { describe, test, it, expect } from "bun:test"; +import { setTimeout, setImmediate } from "node:timers/promises"; + +describe("setTimeout", () => { + it("abort() does not emit global error", async () => { + let unhandledRejectionCaught = false; + + const catchUnhandledRejection = () => { + unhandledRejectionCaught = true; + }; + process.on('unhandledRejection', catchUnhandledRejection); + + const c = new AbortController(); + + global.setTimeout(() => c.abort()); + + await setTimeout(100, undefined, { signal: c.signal }).catch(() => "aborted"); + + // let unhandledRejection to be fired + await setTimeout(100) + + process.off('unhandledRejection', catchUnhandledRejection); + + expect(c.signal.aborted).toBe(true); + expect(unhandledRejectionCaught).toBe(false); + }); +}); + +describe("setImmediate", () => { + it("abort() does not emit global error", async () => { + let unhandledRejectionCaught = false; + + const catchUnhandledRejection = () => { + unhandledRejectionCaught = true; + }; + process.on('unhandledRejection', catchUnhandledRejection); + + const c = new AbortController(); + + global.setImmediate(() => c.abort()); + + await setImmediate(undefined, { signal: c.signal }).catch(() => "aborted"); + + // let unhandledRejection to be fired + await setTimeout(100) + + process.off('unhandledRejection', catchUnhandledRejection); + + expect(c.signal.aborted).toBe(true); + expect(unhandledRejectionCaught).toBe(false); + }); +});