mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
perf(node/async_hooks): optimize AsyncLocalStorage (#19729)
This makes the implementation of "AsyncLocalStorage" from "node:async_hooks" 3.5x faster than before for noop benchmark (measuring baseline overhead). It's still 3.5x slower than not using `AsyncLocalStorage` and 1.64x slower than using noop promise hooks.
This commit is contained in:
parent
57fae55d82
commit
de630b9b78
2 changed files with 19 additions and 11 deletions
|
@ -138,6 +138,15 @@ fn op_is_any_arraybuffer(value: serde_v8::Value) -> bool {
|
|||
value.v8_value.is_array_buffer() || value.v8_value.is_shared_array_buffer()
|
||||
}
|
||||
|
||||
#[op(fast)]
|
||||
fn op_node_is_promise_rejected(value: serde_v8::Value) -> bool {
|
||||
let Ok(promise) = v8::Local::<v8::Promise>::try_from(value.v8_value) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
promise.state() == v8::PromiseState::Rejected
|
||||
}
|
||||
|
||||
deno_core::extension!(deno_node,
|
||||
deps = [ deno_io, deno_fs ],
|
||||
parameters = [P: NodePermissions],
|
||||
|
@ -233,6 +242,7 @@ deno_core::extension!(deno_node,
|
|||
ops::http::op_node_http_request<P>,
|
||||
op_node_build_os,
|
||||
op_is_any_arraybuffer,
|
||||
op_node_is_promise_rejected,
|
||||
ops::require::op_require_init_paths,
|
||||
ops::require::op_require_node_module_paths<P>,
|
||||
ops::require::op_require_proxy_path,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
import { validateFunction } from "ext:deno_node/internal/validators.mjs";
|
||||
|
||||
const { core } = globalThis.__bootstrap;
|
||||
const { ops } = core;
|
||||
|
||||
function assert(cond: boolean) {
|
||||
if (!cond) throw new Error("Assertion failed");
|
||||
|
@ -29,10 +30,6 @@ let rootAsyncFrame: AsyncContextFrame | undefined = undefined;
|
|||
let promiseHooksSet = false;
|
||||
|
||||
const asyncContext = Symbol("asyncContext");
|
||||
function isRejected(promise: Promise<unknown>) {
|
||||
const [state] = core.getPromiseDetails(promise);
|
||||
return state == 2;
|
||||
}
|
||||
|
||||
function setPromiseHooks() {
|
||||
if (promiseHooksSet) {
|
||||
|
@ -43,12 +40,14 @@ function setPromiseHooks() {
|
|||
const init = (promise: Promise<unknown>) => {
|
||||
const currentFrame = AsyncContextFrame.current();
|
||||
if (!currentFrame.isRoot()) {
|
||||
assert(AsyncContextFrame.tryGetContext(promise) == null);
|
||||
if (typeof promise[asyncContext] !== "undefined") {
|
||||
throw new Error("Promise already has async context");
|
||||
}
|
||||
AsyncContextFrame.attachContext(promise);
|
||||
}
|
||||
};
|
||||
const before = (promise: Promise<unknown>) => {
|
||||
const maybeFrame = AsyncContextFrame.tryGetContext(promise);
|
||||
const maybeFrame = promise[asyncContext];
|
||||
if (maybeFrame) {
|
||||
pushAsyncFrame(maybeFrame);
|
||||
} else {
|
||||
|
@ -57,16 +56,16 @@ function setPromiseHooks() {
|
|||
};
|
||||
const after = (promise: Promise<unknown>) => {
|
||||
popAsyncFrame();
|
||||
if (!isRejected(promise)) {
|
||||
if (!ops.op_node_is_promise_rejected(promise)) {
|
||||
// @ts-ignore promise async context
|
||||
delete promise[asyncContext];
|
||||
promise[asyncContext] = undefined;
|
||||
}
|
||||
};
|
||||
const resolve = (promise: Promise<unknown>) => {
|
||||
const currentFrame = AsyncContextFrame.current();
|
||||
if (
|
||||
!currentFrame.isRoot() && isRejected(promise) &&
|
||||
AsyncContextFrame.tryGetContext(promise) == null
|
||||
!currentFrame.isRoot() && ops.op_node_is_promise_rejected(promise) &&
|
||||
typeof promise[asyncContext] === "undefined"
|
||||
) {
|
||||
AsyncContextFrame.attachContext(promise);
|
||||
}
|
||||
|
@ -117,7 +116,6 @@ class AsyncContextFrame {
|
|||
}
|
||||
|
||||
static attachContext(promise: Promise<unknown>) {
|
||||
assert(!(asyncContext in promise));
|
||||
// @ts-ignore promise async context
|
||||
promise[asyncContext] = AsyncContextFrame.current();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue