diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-04-14 02:41:32 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-14 02:41:32 +0200 |
commit | cb2ca234bb39d8e02b08d2866860e8d3a00b5887 (patch) | |
tree | 660044dbb6f6ce62c3f542ade150788870e483a5 /core/01_core.js | |
parent | d192d84a0e0b9954882211b827f17512ad37be7d (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.js | 45 |
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), |