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
This commit is contained in:
Bartek Iwańczuk 2023-04-14 02:41:32 +02:00 committed by GitHub
parent d192d84a0e
commit cb2ca234bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 126 additions and 173 deletions

View file

@ -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),