-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ops): V8 Fast Calls #15291
feat(ops): V8 Fast Calls #15291
Changes from 38 commits
a4892f7
3f89ca3
204f360
94a8a02
bf4e11e
fc89ad2
f0ff99d
125a5c2
c20825b
6295af1
fb91815
537eac9
78bbeb3
4380f7c
4313d89
efcc641
cfa0687
c5cc7e8
278905e
41a701f
b00b7d1
be7ca32
6745546
f08358a
a35028b
66734b7
96f5a59
2463178
ea9b1fb
7ff6211
6ad9eb0
cf5d0ba
35b5fa9
a75ab10
709ce5d
8db574b
87e4036
781816a
5c38093
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,17 +9,39 @@ use crate::modules::ModuleMap; | |
use crate::ops::OpCtx; | ||
use crate::JsRuntime; | ||
use log::debug; | ||
use once_cell::sync::Lazy; | ||
use std::option::Option; | ||
use std::os::raw::c_void; | ||
use v8::fast_api::FastFunction; | ||
use v8::MapFnTo; | ||
|
||
pub static EXTERNAL_REFERENCES: Lazy<v8::ExternalReferences> = | ||
Lazy::new(|| { | ||
v8::ExternalReferences::new(&[v8::ExternalReference { | ||
function: call_console.map_fn_to(), | ||
}]) | ||
}); | ||
pub fn external_references( | ||
ops: &[OpCtx], | ||
snapshot_loaded: bool, | ||
) -> v8::ExternalReferences { | ||
let mut references = vec![v8::ExternalReference { | ||
function: call_console.map_fn_to(), | ||
}]; | ||
|
||
for ctx in ops { | ||
let ctx_ptr = ctx as *const OpCtx as _; | ||
references.push(v8::ExternalReference { pointer: ctx_ptr }); | ||
references.push(v8::ExternalReference { | ||
function: ctx.decl.v8_fn_ptr, | ||
}); | ||
if snapshot_loaded { | ||
if let Some(fast_fn) = &ctx.decl.fast_fn { | ||
references.push(v8::ExternalReference { | ||
pointer: fast_fn.function() as _, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
let refs = v8::ExternalReferences::new(&references); | ||
// Leak, V8 takes ownership of the references. | ||
std::mem::forget(references); | ||
refs | ||
} | ||
|
||
// TODO(nayeemrmn): Move to runtime and/or make `pub(crate)`. | ||
pub fn script_origin<'a>( | ||
|
@@ -82,7 +104,8 @@ pub fn initialize_context<'s>( | |
// Grab the Deno.core.ops object & init it | ||
let ops_obj = JsRuntime::grab_global::<v8::Object>(scope, "Deno.core.ops") | ||
.expect("Deno.core.ops to exist"); | ||
initialize_ops(scope, ops_obj, op_ctxs); | ||
initialize_ops(scope, ops_obj, op_ctxs, snapshot_loaded); | ||
|
||
return scope.escape(context); | ||
} | ||
|
||
|
@@ -94,18 +117,55 @@ pub fn initialize_context<'s>( | |
|
||
// Bind functions to Deno.core.ops.* | ||
let ops_obj = JsRuntime::ensure_objs(scope, global, "Deno.core.ops").unwrap(); | ||
initialize_ops(scope, ops_obj, op_ctxs); | ||
|
||
initialize_ops(scope, ops_obj, op_ctxs, snapshot_loaded); | ||
scope.escape(context) | ||
} | ||
|
||
fn initialize_ops( | ||
scope: &mut v8::HandleScope, | ||
ops_obj: v8::Local<v8::Object>, | ||
op_ctxs: &[OpCtx], | ||
snapshot_loaded: bool, | ||
) { | ||
for ctx in op_ctxs { | ||
let ctx_ptr = ctx as *const OpCtx as *const c_void; | ||
set_func_raw(scope, ops_obj, ctx.decl.name, ctx.decl.v8_fn_ptr, ctx_ptr); | ||
|
||
// If this is a fast op, we don't want it to be in the snapshot. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this explains why the boolean is used here, it does not actually explain why the fast op is not wanted in the snapshot. At least I would like to understand why the fast op is left out: Is it because of the doubling of the |
||
// Only initialize once snapshot is loaded. | ||
if ctx.decl.fast_fn.is_some() && snapshot_loaded { | ||
littledivy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let object_template = v8::ObjectTemplate::new(scope); | ||
assert!(object_template.set_internal_field_count( | ||
(crate::runtime::V8_WRAPPER_OBJECT_INDEX + 1) as usize | ||
)); | ||
|
||
let method_obj = object_template.new_instance(scope).unwrap(); | ||
method_obj.set_aligned_pointer_in_internal_field( | ||
crate::runtime::V8_WRAPPER_OBJECT_INDEX, | ||
ctx_ptr, | ||
); | ||
set_func_raw( | ||
scope, | ||
method_obj, | ||
"fast", | ||
ctx.decl.v8_fn_ptr, | ||
ctx_ptr, | ||
&ctx.decl.fast_fn, | ||
snapshot_loaded, | ||
); | ||
let method_key = v8::String::new(scope, ctx.decl.name).unwrap(); | ||
ops_obj.set(scope, method_key.into(), method_obj.into()); | ||
} else { | ||
set_func_raw( | ||
scope, | ||
ops_obj, | ||
ctx.decl.name, | ||
ctx.decl.v8_fn_ptr, | ||
ctx_ptr, | ||
&None, | ||
snapshot_loaded, | ||
); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -129,13 +189,25 @@ pub fn set_func_raw( | |
name: &'static str, | ||
callback: v8::FunctionCallback, | ||
external_data: *const c_void, | ||
fast_function: &Option<Box<dyn FastFunction>>, | ||
snapshot_loaded: bool, | ||
) { | ||
let key = v8::String::new(scope, name).unwrap(); | ||
let external = v8::External::new(scope, external_data as *mut c_void); | ||
let val = v8::Function::builder_raw(callback) | ||
.data(external.into()) | ||
.build(scope) | ||
.unwrap(); | ||
let builder = | ||
v8::FunctionTemplate::builder_raw(callback).data(external.into()); | ||
let templ = if let Some(fast_function) = fast_function { | ||
// Don't initialize fast ops when snapshotting, the external references count mismatch. | ||
if !snapshot_loaded { | ||
builder.build(scope) | ||
} else { | ||
// TODO(@littledivy): Support fast api overloads in ops. | ||
builder.build_fast(scope, &**fast_function, None) | ||
} | ||
littledivy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
builder.build(scope) | ||
}; | ||
let val = templ.get_function(scope).unwrap(); | ||
val.set_name(key); | ||
obj.set(scope, key.into(), val.into()); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1344,17 +1344,17 @@ async fn op_flash_next_async( | |
// the ContextScope creation is optimized away and the op is as simple as: | ||
// f(info: *const v8::FunctionCallbackInfo) { let rv = ...; rv.set_uint32(op_flash_next()); } | ||
#[op] | ||
fn op_flash_next(op_state: &mut OpState) -> u32 { | ||
let flash_ctx = op_state.borrow_mut::<FlashContext>(); | ||
fn op_flash_next(state: &mut OpState) -> u32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated changes? |
||
let flash_ctx = state.borrow_mut::<FlashContext>(); | ||
let ctx = flash_ctx.servers.get_mut(&0).unwrap(); | ||
next_request_sync(ctx) | ||
} | ||
|
||
// Syncrhonous version of op_flash_next_async. Under heavy load, | ||
// this can collect buffered requests from rx channel and return tokens in a single batch. | ||
#[op] | ||
fn op_flash_next_server(op_state: &mut OpState, server_id: u32) -> u32 { | ||
let flash_ctx = op_state.borrow_mut::<FlashContext>(); | ||
fn op_flash_next_server(state: &mut OpState, server_id: u32) -> u32 { | ||
let flash_ctx = state.borrow_mut::<FlashContext>(); | ||
let ctx = flash_ctx.servers.get_mut(&server_id).unwrap(); | ||
next_request_sync(ctx) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are all this external refs now suddenly needed when previously they were not?