1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

perf(core): speed up promise hook dispatch (#17616)

This commit is contained in:
Bert Belder 2023-02-10 13:06:19 +01:00 committed by GitHub
parent ed3a7ce2f7
commit c83abb80fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 48 deletions

View file

@ -326,41 +326,76 @@
}
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);
if (!promiseHooks.hasBeenSet) {
promiseHooks.hasBeenSet = true;
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);
}
});
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}`);
}
// Add the hook to the list.
ArrayPrototypePush(promiseHooks[i], hook);
}
const wrappedHooks = ArrayPrototypeMap(promiseHooks, (hooks) => {
switch (hooks.length) {
case 0:
return undefined;
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);
}
// 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 function (promise, parent) {
hook1(promise, parent);
hook2(promise, parent);
};
}
function create3xHookWrapper(hook1, hook2, hook3) {
return function (promise, parent) {
hook1(promise, parent);
hook2(promise, parent);
hook3(promise, parent);
};
}
function createHookListWrapper(hooks) {
return function (promise, parent) {
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],
);
}
// Extra Deno.core.* exports

View file

@ -595,25 +595,27 @@ fn op_get_promise_details<'a>(
#[op(v8)]
fn op_set_promise_hooks(
scope: &mut v8::HandleScope,
init_cb: serde_v8::Value,
before_cb: serde_v8::Value,
after_cb: serde_v8::Value,
resolve_cb: serde_v8::Value,
init_hook: serde_v8::Value,
before_hook: serde_v8::Value,
after_hook: serde_v8::Value,
resolve_hook: serde_v8::Value,
) -> Result<(), Error> {
let init_hook_global = to_v8_fn(scope, init_cb)?;
let before_hook_global = to_v8_fn(scope, before_cb)?;
let after_hook_global = to_v8_fn(scope, after_cb)?;
let resolve_hook_global = to_v8_fn(scope, resolve_cb)?;
let init_hook = v8::Local::new(scope, init_hook_global);
let before_hook = v8::Local::new(scope, before_hook_global);
let after_hook = v8::Local::new(scope, after_hook_global);
let resolve_hook = v8::Local::new(scope, resolve_hook_global);
let v8_fns = [init_hook, before_hook, after_hook, resolve_hook]
.into_iter()
.enumerate()
.filter(|(_, hook)| !hook.v8_value.is_undefined())
.try_fold([None; 4], |mut v8_fns, (i, hook)| {
let v8_fn = v8::Local::<v8::Function>::try_from(hook.v8_value)
.map_err(|err| type_error(err.to_string()))?;
v8_fns[i] = Some(v8_fn);
Ok::<_, Error>(v8_fns)
})?;
scope.get_current_context().set_promise_hooks(
Some(init_hook),
Some(before_hook),
Some(after_hook),
Some(resolve_hook),
v8_fns[0], // init
v8_fns[1], // before
v8_fns[2], // after
v8_fns[3], // resolve
);
Ok(())