summaryrefslogtreecommitdiff
path: root/core/01_core.js
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-04-14 02:41:32 +0200
committerGitHub <noreply@github.com>2023-04-14 02:41:32 +0200
commitcb2ca234bb39d8e02b08d2866860e8d3a00b5887 (patch)
tree660044dbb6f6ce62c3f542ade150788870e483a5 /core/01_core.js
parentd192d84a0e0b9954882211b827f17512ad37be7d (diff)
refactor(core): limit number of boundary crossings between Rust and V8 (#18652)
This commit refactors "deno_core" to do fewer boundary crossings from Rust to V8. In other words we are now calling V8 from Rust fewer times. This is done by merging 3 distinct callbacks into a single one. Instead of having "op resolve" callback, "next tick" callback and "macrotask queue" callback, we now have only "Deno.core.eventLoopTick" callback, which is responsible for doing the same actions previous 3 callbacks. On each of the event loop we were doing at least 2 boundary crosses (timers macrotask queue callback and unhandled promise rejection callback) and up to 4 crosses if there were op response and next tick callbacks coming from Node.js compatibility layer. Now this is all done in a single callback. Closes https://github.com/denoland/deno/issues/18620
Diffstat (limited to 'core/01_core.js')
-rw-r--r--core/01_core.js45
1 files changed, 40 insertions, 5 deletions
diff --git a/core/01_core.js b/core/01_core.js
index 4cefb52e9..6231c0766 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -133,13 +133,48 @@
return promiseRing[idx] != NO_PROMISE;
}
- function opresolve() {
- for (let i = 0; i < arguments.length; i += 2) {
+ const macrotaskCallbacks = [];
+ const nextTickCallbacks = [];
+
+ function setMacrotaskCallback(cb) {
+ ArrayPrototypePush(macrotaskCallbacks, cb);
+ }
+
+ function setNextTickCallback(cb) {
+ ArrayPrototypePush(nextTickCallbacks, cb);
+ }
+
+ // This function has variable number of arguments. The last argument describes
+ // if there's a "next tick" scheduled by the Node.js compat layer. Arguments
+ // before last are alternating integers and any values that describe the
+ // responses of async ops.
+ function eventLoopTick() {
+ // First respond to all pending ops.
+ for (let i = 0; i < arguments.length - 1; i += 2) {
const promiseId = arguments[i];
const res = arguments[i + 1];
const promise = getPromise(promiseId);
promise.resolve(res);
}
+ // Drain nextTick queue if there's a tick scheduled.
+ if (arguments[arguments.length - 1]) {
+ for (let i = 0; i < nextTickCallbacks.length; i++) {
+ nextTickCallbacks[i]();
+ }
+ } else {
+ ops.op_run_microtasks();
+ }
+ // Finally drain macrotask queue.
+ for (let i = 0; i < macrotaskCallbacks.length; i++) {
+ const cb = macrotaskCallbacks[i];
+ while (true) {
+ const res = cb();
+ ops.op_run_microtasks();
+ if (res === true) {
+ break;
+ }
+ }
+ }
}
function registerErrorClass(className, errorClass) {
@@ -406,7 +441,7 @@
registerErrorBuilder,
registerErrorClass,
buildCustomError,
- opresolve,
+ eventLoopTick,
BadResource,
BadResourcePrototype,
Interrupted,
@@ -428,8 +463,8 @@
writeSync: (rid, buffer) => ops.op_write_sync(rid, buffer),
shutdown: opAsync.bind(null, "op_shutdown"),
print: (msg, isErr) => ops.op_print(msg, isErr),
- setMacrotaskCallback: (fn) => ops.op_set_macrotask_callback(fn),
- setNextTickCallback: (fn) => ops.op_set_next_tick_callback(fn),
+ setMacrotaskCallback,
+ setNextTickCallback,
runMicrotasks: () => ops.op_run_microtasks(),
hasTickScheduled: () => ops.op_has_tick_scheduled(),
setHasTickScheduled: (bool) => ops.op_set_has_tick_scheduled(bool),