summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/dts/lib.deno.unstable.d.ts12
-rw-r--r--cli/tests/unit/timers_test.ts120
-rw-r--r--ext/timers/01_timers.js56
-rw-r--r--runtime/js/90_deno_ns.js2
4 files changed, 177 insertions, 13 deletions
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index fd62a9486..e675b6347 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -1022,6 +1022,18 @@ declare namespace Deno {
* Release an advisory file-system lock for the provided file.
*/
export function funlockSync(rid: number): void;
+
+ /** **UNSTABLE**: new API, yet to be vetted.
+ *
+ * Make the timer of the given id blocking the event loop from finishing
+ */
+ export function refTimer(id: number): void;
+
+ /** **UNSTABLE**: new API, yet to be vetted.
+ *
+ * Make the timer of the given id not blocking the event loop from finishing
+ */
+ export function unrefTimer(id: number): void;
}
declare function fetch(
diff --git a/cli/tests/unit/timers_test.ts b/cli/tests/unit/timers_test.ts
index fc6f9af4d..ec9b6f757 100644
--- a/cli/tests/unit/timers_test.ts
+++ b/cli/tests/unit/timers_test.ts
@@ -9,6 +9,8 @@ import {
unreachable,
} from "./test_util.ts";
+const decoder = new TextDecoder();
+
Deno.test(async function functionParameterBindingSuccess() {
const promise = deferred();
let count = 0;
@@ -573,3 +575,121 @@ Deno.test(
await p;
},
);
+
+async function execCode(code: string) {
+ const p = Deno.run({
+ cmd: [
+ Deno.execPath(),
+ "eval",
+ "--unstable",
+ code,
+ ],
+ stdout: "piped",
+ });
+ const [status, output] = await Promise.all([p.status(), p.output()]);
+ p.close();
+ return [status.code, decoder.decode(output)];
+}
+
+Deno.test({
+ name: "unrefTimer",
+ permissions: { run: true },
+ fn: async () => {
+ const [statusCode, output] = await execCode(`
+ const timer = setTimeout(() => console.log("1"));
+ Deno.unrefTimer(timer);
+ `);
+ assertEquals(statusCode, 0);
+ assertEquals(output, "");
+ },
+});
+
+Deno.test({
+ name: "unrefTimer - mix ref and unref 1",
+ permissions: { run: true },
+ fn: async () => {
+ const [statusCode, output] = await execCode(`
+ const timer1 = setTimeout(() => console.log("1"), 200);
+ const timer2 = setTimeout(() => console.log("2"), 400);
+ const timer3 = setTimeout(() => console.log("3"), 600);
+ Deno.unrefTimer(timer3);
+ `);
+ assertEquals(statusCode, 0);
+ assertEquals(output, "1\n2\n");
+ },
+});
+
+Deno.test({
+ name: "unrefTimer - mix ref and unref 2",
+ permissions: { run: true },
+ fn: async () => {
+ const [statusCode, output] = await execCode(`
+ const timer1 = setTimeout(() => console.log("1"), 200);
+ const timer2 = setTimeout(() => console.log("2"), 400);
+ const timer3 = setTimeout(() => console.log("3"), 600);
+ Deno.unrefTimer(timer1);
+ Deno.unrefTimer(timer2);
+ `);
+ assertEquals(statusCode, 0);
+ assertEquals(output, "1\n2\n3\n");
+ },
+});
+
+Deno.test({
+ name: "unrefTimer - unref interval",
+ permissions: { run: true },
+ fn: async () => {
+ const [statusCode, output] = await execCode(`
+ let i = 0;
+ const timer1 = setInterval(() => {
+ console.log("1");
+ i++;
+ if (i === 5) {
+ Deno.unrefTimer(timer1);
+ }
+ }, 10);
+ `);
+ assertEquals(statusCode, 0);
+ assertEquals(output, "1\n1\n1\n1\n1\n");
+ },
+});
+
+Deno.test({
+ name: "unrefTimer - unref then ref 1",
+ permissions: { run: true },
+ fn: async () => {
+ const [statusCode, output] = await execCode(`
+ const timer1 = setTimeout(() => console.log("1"), 10);
+ Deno.unrefTimer(timer1);
+ Deno.refTimer(timer1);
+ `);
+ assertEquals(statusCode, 0);
+ assertEquals(output, "1\n");
+ },
+});
+
+Deno.test({
+ name: "unrefTimer - unref then ref",
+ permissions: { run: true },
+ fn: async () => {
+ const [statusCode, output] = await execCode(`
+ const timer1 = setTimeout(() => {
+ console.log("1");
+ Deno.refTimer(timer2);
+ }, 10);
+ const timer2 = setTimeout(() => console.log("2"), 20);
+ Deno.unrefTimer(timer2);
+ `);
+ assertEquals(statusCode, 0);
+ assertEquals(output, "1\n2\n");
+ },
+});
+
+Deno.test({
+ name: "unrefTimer - invalid calls do nothing",
+ permissions: { run: true },
+ fn: () => {
+ Deno.unrefTimer(NaN);
+ Deno.refTimer(NaN);
+ },
+});
diff --git a/ext/timers/01_timers.js b/ext/timers/01_timers.js
index ee3d85661..6bed6ba4c 100644
--- a/ext/timers/01_timers.js
+++ b/ext/timers/01_timers.js
@@ -16,6 +16,7 @@
// deno-lint-ignore camelcase
NumberPOSITIVE_INFINITY,
PromisePrototypeThen,
+ SymbolFor,
TypeError,
} = window.__bootstrap.primordials;
const { webidl } = window.__bootstrap;
@@ -87,7 +88,7 @@
* The keys in this map correspond to the key ID's in the spec's map of active
* timers. The values are the timeout's cancel rid.
*
- * @type {Map<number, number>}
+ * @type {Map<number, { cancelRid: number, isRef: boolean, promiseId: number }>}
*/
const activeTimers = new Map();
@@ -112,20 +113,21 @@
// previousId be an implementation-defined integer than is greater than zero
// and does not already exist in global's map of active timers.
let id;
- let cancelRid;
+ let timerInfo;
if (prevId !== undefined) {
// `prevId` is only passed for follow-up calls on intervals
assert(repeat);
id = prevId;
- cancelRid = MapPrototypeGet(activeTimers, id);
+ timerInfo = MapPrototypeGet(activeTimers, id);
} else {
// TODO(@andreubotella): Deal with overflow.
// https://github.com/whatwg/html/issues/7358
id = nextId++;
- cancelRid = core.opSync("op_timer_handle");
+ const cancelRid = core.opSync("op_timer_handle");
+ timerInfo = { cancelRid, isRef: true, promiseId: -1 };
// Step 4 in "run steps after a timeout".
- MapPrototypeSet(activeTimers, id, cancelRid);
+ MapPrototypeSet(activeTimers, id, timerInfo);
}
// 3. If the surrounding agent's event loop's currently running task is a
@@ -175,7 +177,7 @@
}
} else {
// 6. Otherwise, remove global's map of active timers[id].
- core.tryClose(cancelRid);
+ core.tryClose(timerInfo.cancelRid);
MapPrototypeDelete(activeTimers, id);
}
},
@@ -192,7 +194,7 @@
runAfterTimeout(
() => ArrayPrototypePush(timerTasks, task),
timeout,
- cancelRid,
+ timerInfo,
);
return id;
@@ -219,9 +221,17 @@
* @param {() => void} cb Will be run after the timeout, if it hasn't been
* cancelled.
* @param {number} millis
- * @param {number} cancelRid
+ * @param {{ cancelRid: number, isRef: boolean, promiseId: number }} timerInfo
*/
- function runAfterTimeout(cb, millis, cancelRid) {
+ function runAfterTimeout(cb, millis, timerInfo) {
+ const cancelRid = timerInfo.cancelRid;
+ const sleepPromise = core.opAsync("op_sleep", millis, cancelRid);
+ timerInfo.promiseId =
+ sleepPromise[SymbolFor("Deno.core.internalPromiseId")];
+ if (!timerInfo.isRef) {
+ core.unrefOp(timerInfo.promiseId);
+ }
+
/** @type {ScheduledTimer} */
const timerObject = {
millis,
@@ -242,7 +252,7 @@
// 1.
PromisePrototypeThen(
- core.opAsync("op_sleep", millis, cancelRid),
+ sleepPromise,
() => {
// 2. Wait until any invocations of this algorithm that had the same
// global and orderingIdentifier, that started before this one, and
@@ -334,9 +344,9 @@
function clearTimeout(id = 0) {
checkThis(this);
id = webidl.converters.long(id);
- const cancelHandle = MapPrototypeGet(activeTimers, id);
- if (cancelHandle !== undefined) {
- core.tryClose(cancelHandle);
+ const timerInfo = MapPrototypeGet(activeTimers, id);
+ if (timerInfo !== undefined) {
+ core.tryClose(timerInfo.cancelRid);
MapPrototypeDelete(activeTimers, id);
}
}
@@ -346,6 +356,24 @@
clearTimeout(id);
}
+ function refTimer(id) {
+ const timerInfo = MapPrototypeGet(activeTimers, id);
+ if (timerInfo === undefined || timerInfo.isRef) {
+ return;
+ }
+ timerInfo.isRef = true;
+ core.refOp(timerInfo.promiseId);
+ }
+
+ function unrefTimer(id) {
+ const timerInfo = MapPrototypeGet(activeTimers, id);
+ if (timerInfo === undefined || !timerInfo.isRef) {
+ return;
+ }
+ timerInfo.isRef = false;
+ core.unrefOp(timerInfo.promiseId);
+ }
+
window.__bootstrap.timers = {
setTimeout,
setInterval,
@@ -354,5 +382,7 @@
handleTimerMacrotask,
opNow,
sleepSync,
+ refTimer,
+ unrefTimer,
};
})(this);
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index f858a93eb..e5e52b1f6 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -139,5 +139,7 @@
flockSync: __bootstrap.fs.flockSync,
funlock: __bootstrap.fs.funlock,
funlockSync: __bootstrap.fs.funlockSync,
+ refTimer: __bootstrap.timers.refTimer,
+ unrefTimer: __bootstrap.timers.unrefTimer,
};
})(this);