mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(core): remove async op inlining optimization (#17899)
Runtime generation of async op wrappers contributed to increased startup time and core became unusable with `--disallow-code-generation-from-strings` flag. The optimization only affects very small microbenchmarks so this revert will not cause any regressions.
This commit is contained in:
parent
4773d07974
commit
da781280b8
6 changed files with 42 additions and 192 deletions
|
@ -104,9 +104,6 @@ pub fn bench_js_async_with(
|
|||
};
|
||||
let looped = loop_code(inner_iters, src);
|
||||
let src = looped.as_ref();
|
||||
runtime
|
||||
.execute_script("init", "Deno.core.initializeAsyncOps();")
|
||||
.unwrap();
|
||||
if is_profiling() {
|
||||
for _ in 0..opts.profiling_outer {
|
||||
tokio_runtime.block_on(inner_async(src, &mut runtime));
|
||||
|
|
|
@ -10,24 +10,19 @@
|
|||
TypeError,
|
||||
URIError,
|
||||
Array,
|
||||
ArrayFrom,
|
||||
ArrayPrototypeFill,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeMap,
|
||||
ErrorCaptureStackTrace,
|
||||
Function,
|
||||
Promise,
|
||||
ObjectAssign,
|
||||
ObjectFromEntries,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
Map,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeDelete,
|
||||
MapPrototypeSet,
|
||||
PromisePrototypeThen,
|
||||
ReflectApply,
|
||||
SafePromisePrototypeFinally,
|
||||
StringPrototypeSlice,
|
||||
SymbolFor,
|
||||
|
@ -175,61 +170,20 @@
|
|||
return nextPromiseId++;
|
||||
}
|
||||
|
||||
// Generate async op wrappers. See core/bindings.rs
|
||||
function initializeAsyncOps() {
|
||||
function genAsyncOp(op, name, args) {
|
||||
return new Function(
|
||||
"setPromise",
|
||||
"getPromise",
|
||||
"promiseIdSymbol",
|
||||
"rollPromiseId",
|
||||
"handleOpCallTracing",
|
||||
"op",
|
||||
"unwrapOpResult",
|
||||
"PromisePrototypeThen",
|
||||
`
|
||||
return function ${name}(${args}) {
|
||||
const id = rollPromiseId();
|
||||
let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
|
||||
try {
|
||||
op(id, ${args});
|
||||
} catch (err) {
|
||||
// Cleanup the just-created promise
|
||||
getPromise(id);
|
||||
// Rethrow the error
|
||||
throw err;
|
||||
}
|
||||
promise = handleOpCallTracing("${name}", id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
}
|
||||
`,
|
||||
)(
|
||||
setPromise,
|
||||
getPromise,
|
||||
promiseIdSymbol,
|
||||
rollPromiseId,
|
||||
handleOpCallTracing,
|
||||
op,
|
||||
unwrapOpResult,
|
||||
PromisePrototypeThen,
|
||||
);
|
||||
}
|
||||
|
||||
// { <name>: <argc>, ... }
|
||||
const info = ops.asyncOpsInfo();
|
||||
for (const name in info) {
|
||||
if (!ObjectPrototypeHasOwnProperty(info, name)) {
|
||||
continue;
|
||||
}
|
||||
const argc = info[name];
|
||||
const op = ops[name];
|
||||
const args = ArrayPrototypeJoin(
|
||||
ArrayFrom({ length: argc }, (_, i) => `arg${i}`),
|
||||
", ",
|
||||
);
|
||||
ops[name] = genAsyncOp(op, name, args);
|
||||
function opAsync(name, ...args) {
|
||||
const id = rollPromiseId();
|
||||
let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
|
||||
try {
|
||||
ops[name](id, ...args);
|
||||
} catch (err) {
|
||||
// Cleanup the just-created promise
|
||||
getPromise(id);
|
||||
// Rethrow the error
|
||||
throw err;
|
||||
}
|
||||
promise = handleOpCallTracing(name, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
}
|
||||
|
||||
function handleOpCallTracing(opName, promiseId, p) {
|
||||
|
@ -245,10 +199,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function opAsync(opName, ...args) {
|
||||
return ReflectApply(ops[opName], ops, args);
|
||||
}
|
||||
|
||||
function refOp(promiseId) {
|
||||
if (!hasPromise(promiseId)) {
|
||||
return;
|
||||
|
@ -401,7 +351,6 @@
|
|||
// Extra Deno.core.* exports
|
||||
const core = ObjectAssign(globalThis.Deno.core, {
|
||||
opAsync,
|
||||
initializeAsyncOps,
|
||||
resources,
|
||||
metrics,
|
||||
registerErrorBuilder,
|
||||
|
@ -421,11 +370,11 @@
|
|||
setPromiseHooks,
|
||||
close: (rid) => ops.op_close(rid),
|
||||
tryClose: (rid) => ops.op_try_close(rid),
|
||||
read: (rid, buffer) => ops.op_read(rid, buffer),
|
||||
readAll: (rid) => ops.op_read_all(rid),
|
||||
write: (rid, buffer) => ops.op_write(rid, buffer),
|
||||
writeAll: (rid, buffer) => ops.op_write_all(rid, buffer),
|
||||
shutdown: (rid) => ops.op_shutdown(rid),
|
||||
read: opAsync.bind(null, "op_read"),
|
||||
readAll: opAsync.bind(null, "op_read_all"),
|
||||
write: opAsync.bind(null, "op_write"),
|
||||
writeAll: opAsync.bind(null, "op_write_all"),
|
||||
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),
|
||||
|
|
|
@ -135,9 +135,6 @@ pub fn initialize_context<'s>(
|
|||
.try_into()
|
||||
.unwrap();
|
||||
initialize_ops(scope, ops_obj, op_ctxs, snapshot_options);
|
||||
if snapshot_options != SnapshotOptions::CreateFromExisting {
|
||||
initialize_async_ops_info(scope, ops_obj, op_ctxs);
|
||||
}
|
||||
return scope.escape(context);
|
||||
}
|
||||
|
||||
|
@ -161,9 +158,6 @@ pub fn initialize_context<'s>(
|
|||
let ops_obj = v8::Object::new(scope);
|
||||
core_obj.set(scope, ops_str.into(), ops_obj.into());
|
||||
|
||||
if !snapshot_options.will_snapshot() {
|
||||
initialize_async_ops_info(scope, ops_obj, op_ctxs);
|
||||
}
|
||||
initialize_ops(scope, ops_obj, op_ctxs, snapshot_options);
|
||||
scope.escape(context)
|
||||
}
|
||||
|
@ -657,84 +651,3 @@ pub fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef<str>) {
|
|||
let exception = v8::Exception::type_error(scope, message);
|
||||
scope.throw_exception(exception);
|
||||
}
|
||||
|
||||
struct AsyncOpsInfo {
|
||||
ptr: *const OpCtx,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'s> IntoIterator for &'s AsyncOpsInfo {
|
||||
type Item = &'s OpCtx;
|
||||
type IntoIter = AsyncOpsInfoIterator<'s>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
AsyncOpsInfoIterator {
|
||||
// SAFETY: OpCtx slice is valid for the lifetime of the Isolate
|
||||
info: unsafe { std::slice::from_raw_parts(self.ptr, self.len) },
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncOpsInfoIterator<'s> {
|
||||
info: &'s [OpCtx],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'s> Iterator for AsyncOpsInfoIterator<'s> {
|
||||
type Item = &'s OpCtx;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
match self.info.get(self.index) {
|
||||
Some(ctx) if ctx.decl.is_async => {
|
||||
self.index += 1;
|
||||
return Some(ctx);
|
||||
}
|
||||
Some(_) => {
|
||||
self.index += 1;
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn async_ops_info(
|
||||
scope: &mut v8::HandleScope,
|
||||
args: v8::FunctionCallbackArguments,
|
||||
mut rv: v8::ReturnValue,
|
||||
) {
|
||||
let async_op_names = v8::Object::new(scope);
|
||||
let external: v8::Local<v8::External> = args.data().try_into().unwrap();
|
||||
let info: &AsyncOpsInfo =
|
||||
// SAFETY: external is guaranteed to be a valid pointer to AsyncOpsInfo
|
||||
unsafe { &*(external.value() as *const AsyncOpsInfo) };
|
||||
for ctx in info {
|
||||
let name = v8::String::new(scope, ctx.decl.name).unwrap();
|
||||
let argc = v8::Integer::new(scope, ctx.decl.argc as i32);
|
||||
async_op_names.set(scope, name.into(), argc.into());
|
||||
}
|
||||
rv.set(async_op_names.into());
|
||||
}
|
||||
|
||||
fn initialize_async_ops_info(
|
||||
scope: &mut v8::HandleScope,
|
||||
ops_obj: v8::Local<v8::Object>,
|
||||
op_ctxs: &[OpCtx],
|
||||
) {
|
||||
let key = v8::String::new(scope, "asyncOpsInfo").unwrap();
|
||||
let external = v8::External::new(
|
||||
scope,
|
||||
Box::into_raw(Box::new(AsyncOpsInfo {
|
||||
ptr: op_ctxs as *const [OpCtx] as _,
|
||||
len: op_ctxs.len(),
|
||||
})) as *mut c_void,
|
||||
);
|
||||
let val = v8::Function::builder(async_ops_info)
|
||||
.data(external.into())
|
||||
.build(scope)
|
||||
.unwrap();
|
||||
val.set_name(key);
|
||||
ops_obj.set(scope, key.into(), val.into());
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
// This is not a real HTTP server. We read blindly one time into 'requestBuf',
|
||||
// then write this fixed 'responseBuf'. The point of this benchmark is to
|
||||
// exercise the event loop in a simple yet semi-realistic way.
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
const { ops } = Deno.core;
|
||||
const { ops, opAsync } = Deno.core;
|
||||
|
||||
const requestBuf = new Uint8Array(64 * 1024);
|
||||
const responseBuf = new Uint8Array(
|
||||
|
@ -20,11 +19,11 @@ function listen() {
|
|||
|
||||
/** Accepts a connection, returns rid. */
|
||||
function accept(serverRid) {
|
||||
return ops.op_accept(serverRid);
|
||||
return opAsync("op_accept", serverRid);
|
||||
}
|
||||
|
||||
function read(serverRid, buf) {
|
||||
return ops.op_read_socket(serverRid, buf);
|
||||
return opAsync("op_read_socket", serverRid, buf);
|
||||
}
|
||||
|
||||
async function serve(rid) {
|
||||
|
|
|
@ -322,12 +322,6 @@ impl SnapshotOptions {
|
|||
SnapshotOptions::Load | SnapshotOptions::CreateFromExisting
|
||||
)
|
||||
}
|
||||
pub fn will_snapshot(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
SnapshotOptions::Create | SnapshotOptions::CreateFromExisting
|
||||
)
|
||||
}
|
||||
|
||||
fn from_bools(snapshot_loaded: bool, will_snapshot: bool) -> Self {
|
||||
match (snapshot_loaded, will_snapshot) {
|
||||
|
@ -2923,10 +2917,10 @@ pub mod tests {
|
|||
.execute_script(
|
||||
"filename.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
var promiseIdSymbol = Symbol.for("Deno.core.internalPromiseId");
|
||||
var p1 = Deno.core.ops.op_test(42);
|
||||
var p2 = Deno.core.ops.op_test(42);
|
||||
var p1 = Deno.core.opAsync("op_test", 42);
|
||||
var p2 = Deno.core.opAsync("op_test", 42);
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -2979,7 +2973,7 @@ pub mod tests {
|
|||
"filename.js",
|
||||
r#"
|
||||
let control = 42;
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
Deno.core.opAsync("op_test", control);
|
||||
async function main() {
|
||||
Deno.core.opAsync("op_test", control);
|
||||
|
@ -2998,7 +2992,7 @@ pub mod tests {
|
|||
.execute_script(
|
||||
"filename.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
const p = Deno.core.opAsync("op_test", 42);
|
||||
if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) {
|
||||
throw new Error("missing id on returned promise");
|
||||
|
@ -3015,7 +3009,7 @@ pub mod tests {
|
|||
.execute_script(
|
||||
"filename.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
Deno.core.opAsync("op_test");
|
||||
"#,
|
||||
)
|
||||
|
@ -3030,7 +3024,7 @@ pub mod tests {
|
|||
.execute_script(
|
||||
"filename.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
let zero_copy_a = new Uint8Array([0]);
|
||||
Deno.core.opAsync("op_test", null, zero_copy_a);
|
||||
"#,
|
||||
|
@ -3904,7 +3898,7 @@ if (errMessage !== "higher-level sync error: original sync error") {
|
|||
.execute_script(
|
||||
"test_error_context_async.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
(async () => {
|
||||
let errMessage;
|
||||
try {
|
||||
|
@ -4059,7 +4053,7 @@ assertEquals(1, notify_return_value);
|
|||
runtime
|
||||
.execute_script(
|
||||
"op_async_borrow.js",
|
||||
"Deno.core.initializeAsyncOps(); Deno.core.ops.op_async_borrow()",
|
||||
"Deno.core.opAsync(\"op_async_borrow\")",
|
||||
)
|
||||
.unwrap();
|
||||
runtime.run_event_loop(false).await.unwrap();
|
||||
|
@ -4133,8 +4127,8 @@ Deno.core.ops.op_sync_serialize_object_with_numbers_as_keys({
|
|||
.execute_script(
|
||||
"op_async_serialize_object_with_numbers_as_keys.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
||||
|
||||
Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
|
||||
lines: {
|
||||
100: {
|
||||
unit: "m"
|
||||
|
@ -4172,7 +4166,7 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
|||
.execute_script(
|
||||
"macrotasks_and_nextticks.js",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
(async function () {
|
||||
const results = [];
|
||||
Deno.core.ops.op_set_macrotask_callback(() => {
|
||||
|
@ -4440,12 +4434,12 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
|||
"",
|
||||
&format!(
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
globalThis.rejectValue = undefined;
|
||||
Deno.core.setPromiseRejectCallback((_type, _promise, reason) => {{
|
||||
globalThis.rejectValue = `{realm_name}/${{reason}}`;
|
||||
}});
|
||||
Deno.core.ops.op_void_async().then(() => Promise.reject({number}));
|
||||
Deno.core.opAsync("op_void_async").then(() => Promise.reject({number}));
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -4876,12 +4870,12 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
|||
runtime.v8_isolate(),
|
||||
"",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
|
||||
(async function () {
|
||||
const buf = await Deno.core.ops.op_test(false);
|
||||
const buf = await Deno.core.opAsync("op_test", false);
|
||||
let err;
|
||||
try {
|
||||
await Deno.core.ops.op_test(true);
|
||||
await Deno.core.opAsync("op_test", true);
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
|
@ -4930,8 +4924,8 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
|||
runtime.v8_isolate(),
|
||||
"",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
var promise = Deno.core.ops.op_pending();
|
||||
|
||||
var promise = Deno.core.opAsync("op_pending");
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -4940,8 +4934,8 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
|||
runtime.v8_isolate(),
|
||||
"",
|
||||
r#"
|
||||
Deno.core.initializeAsyncOps();
|
||||
var promise = Deno.core.ops.op_pending();
|
||||
|
||||
var promise = Deno.core.opAsync("op_pending");
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -390,7 +390,6 @@ function bootstrapMainRuntime(runtimeOptions) {
|
|||
throw new Error("Worker runtime already bootstrapped");
|
||||
}
|
||||
|
||||
core.initializeAsyncOps();
|
||||
performance.setTimeOrigin(DateNow());
|
||||
globalThis_ = globalThis;
|
||||
|
||||
|
@ -523,7 +522,6 @@ function bootstrapWorkerRuntime(
|
|||
throw new Error("Worker runtime already bootstrapped");
|
||||
}
|
||||
|
||||
core.initializeAsyncOps();
|
||||
performance.setTimeOrigin(DateNow());
|
||||
globalThis_ = globalThis;
|
||||
|
||||
|
|
Loading…
Reference in a new issue