Skip to content

Commit

Permalink
perf(core): speed up promise hook dispatch
Browse files Browse the repository at this point in the history
  • Loading branch information
piscisaureus committed Feb 1, 2023
1 parent 690b6ac commit 1abc19f
Showing 1 changed file with 77 additions and 43 deletions.
120 changes: 77 additions & 43 deletions core/01_core.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ArrayPrototypeMap,
ErrorCaptureStackTrace,
Function,
FunctionPrototypeToString,
Promise,
ObjectAssign,
ObjectFromEntries,
Expand All @@ -28,6 +29,7 @@
MapPrototypeSet,
PromisePrototypeThen,
ReflectApply,
RegExpPrototypeTest,
SafePromisePrototypeFinally,
StringPrototypeSlice,
SymbolFor,
Expand Down Expand Up @@ -326,43 +328,84 @@
}
const InterruptedPrototype = Interrupted.prototype;

const promiseHooks = {
init: [],
before: [],
after: [],
resolve: [],
hasBeenSet: false,
};
const promiseHooks = [
[], // init
[], // before
[], // after
[], // resolve
];

function setPromiseHooks(init, before, after, resolve) {
if (init) ArrayPrototypePush(promiseHooks.init, init);
if (before) ArrayPrototypePush(promiseHooks.before, before);
if (after) ArrayPrototypePush(promiseHooks.after, after);
if (resolve) ArrayPrototypePush(promiseHooks.resolve, resolve);
const hooks = [init, before, after, resolve];
for (let i = 0; i < hooks.length; i++) {
const hook = hooks[i];
// Skip if no callback was provided for this hook type.
if (hook == null) {
continue;
}
// Verify that the type of `hook` is a function.
if (typeof hook !== "function") {
throw new TypeError(`Expected function at position ${i}`);
}
// Crudely filter out no-op functions.
if (
RegExpPrototypeTest(/\)(?:\s*|=>){}$/, FunctionPrototypeToString(hook))
) {
continue;
}
// Add the material hook to the list.
ArrayPrototypePush(promiseHooks[i], hook);
}

if (!promiseHooks.hasBeenSet) {
promiseHooks.hasBeenSet = true;
const wrappedHooks = ArrayPrototypeMap(promiseHooks, (hooks) => {
switch (hooks.length) {
case 0:
return noop;
case 1:
return hooks[0];
case 2:
return create2xHookWrapper(hooks[0], hooks[1]);
case 3:
return create3xHookWrapper(hooks[0], hooks[1], hooks[2]);
default:
return createHookListWrapper(hooks);
}

ops.op_set_promise_hooks((promise, parentPromise) => {
for (let i = 0; i < promiseHooks.init.length; ++i) {
promiseHooks.init[i](promise, parentPromise);
}
}, (promise) => {
for (let i = 0; i < promiseHooks.before.length; ++i) {
promiseHooks.before[i](promise);
}
}, (promise) => {
for (let i = 0; i < promiseHooks.after.length; ++i) {
promiseHooks.after[i](promise);
}
}, (promise) => {
for (let i = 0; i < promiseHooks.resolve.length; ++i) {
promiseHooks.resolve[i](promise);
// The following functions are used to create wrapper functions that call
// all the hooks in a list of a certain length. The reason to use a
// function that creates a wrapper is to minimize the number of objects
// captured in the closure.
function create2xHookWrapper(hook1, hook2) {
return (promise, parent) => {
hook1(promise, parent);
hook2(promise, parent);
};
}
function create3xHookWrapper(hook1, hook2, hook3) {
return (promise, parent) => {
hook1(promise, parent);
hook2(promise, parent);
hook3(promise, parent);
};
}
function createHookListWrapper(hooks) {
for (let i = 0; i < hooks.length; i++) {
const hook = hooks[i];
hook(promise, parent);
}
});
}
}
});

ops.op_set_promise_hooks(
wrappedHooks[0],
wrappedHooks[1],
wrappedHooks[2],
wrappedHooks[3],
);
}

function noop() {}

// Extra Deno.core.* exports
const core = ObjectAssign(globalThis.Deno.core, {
opAsync,
Expand Down Expand Up @@ -397,28 +440,19 @@
runMicrotasks: () => ops.op_run_microtasks(),
hasTickScheduled: () => ops.op_has_tick_scheduled(),
setHasTickScheduled: (bool) => ops.op_set_has_tick_scheduled(bool),
evalContext: (
source,
specifier,
) => ops.op_eval_context(source, specifier),
evalContext: (source, specifier) => ops.op_eval_context(source, specifier),
createHostObject: () => ops.op_create_host_object(),
encode: (text) => ops.op_encode(text),
decode: (buffer) => ops.op_decode(buffer),
serialize: (
value,
options,
errorCallback,
) => ops.op_serialize(value, options, errorCallback),
serialize: (value, options, errorCallback) =>
ops.op_serialize(value, options, errorCallback),
deserialize: (buffer, options) => ops.op_deserialize(buffer, options),
getPromiseDetails: (promise) => ops.op_get_promise_details(promise),
getProxyDetails: (proxy) => ops.op_get_proxy_details(proxy),
isProxy: (value) => ops.op_is_proxy(value),
memoryUsage: () => ops.op_memory_usage(),
setWasmStreamingCallback: (fn) => ops.op_set_wasm_streaming_callback(fn),
abortWasmStreaming: (
rid,
error,
) => ops.op_abort_wasm_streaming(rid, error),
abortWasmStreaming: (rid, error) => ops.op_abort_wasm_streaming(rid, error),
destructureError: (error) => ops.op_destructure_error(error),
opNames: () => ops.op_op_names(),
eventLoopHasMoreWork: () => ops.op_event_loop_has_more_work(),
Expand Down

0 comments on commit 1abc19f

Please sign in to comment.