From cb2ca234bb39d8e02b08d2866860e8d3a00b5887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 14 Apr 2023 02:41:32 +0200 Subject: 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 --- core/01_core.js | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) (limited to 'core/01_core.js') 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), -- cgit v1.2.3