summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2023-02-24 01:20:15 +0530
committerGitHub <noreply@github.com>2023-02-23 19:50:15 +0000
commitda781280b8422b4116473b366fb7d207909a31da (patch)
treebef59ac9dd8e26d97d7d494de87d53ab537724cc
parent4773d07974167b565ba84d5603a6d6f70cd12660 (diff)
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.
-rw-r--r--bench_util/js_runtime.rs3
-rw-r--r--core/01_core.js87
-rw-r--r--core/bindings.rs87
-rw-r--r--core/examples/http_bench_json_ops/http_bench_json_ops.js7
-rw-r--r--core/runtime.rs48
-rw-r--r--runtime/js/99_main.js2
6 files changed, 42 insertions, 192 deletions
diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs
index 376d30593..e45af2bdd 100644
--- a/bench_util/js_runtime.rs
+++ b/bench_util/js_runtime.rs
@@ -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));
diff --git a/core/01_core.js b/core/01_core.js
index bea9c4290..5a622b0ea 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -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),
diff --git a/core/bindings.rs b/core/bindings.rs
index 8ac308250..aa42c2b77 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -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());
-}
diff --git a/core/examples/http_bench_json_ops/http_bench_json_ops.js b/core/examples/http_bench_json_ops/http_bench_json_ops.js
index 9650804c7..5a205188b 100644
--- a/core/examples/http_bench_json_ops/http_bench_json_ops.js
+++ b/core/examples/http_bench_json_ops/http_bench_json_ops.js
@@ -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) {
diff --git a/core/runtime.rs b/core/runtime.rs
index 1f325ad32..81314f0bf 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -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();
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index da5b5f1b8..ffb479c32 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -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;