summaryrefslogtreecommitdiff
path: root/core/runtime.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/runtime.rs')
-rw-r--r--core/runtime.rs201
1 files changed, 59 insertions, 142 deletions
diff --git a/core/runtime.rs b/core/runtime.rs
index b03f3f7d0..bf321e055 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -158,8 +158,6 @@ pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
pub struct JsRuntimeState {
global_realm: Option<JsRealm>,
known_realms: Vec<v8::Weak<v8::Context>>,
- pub(crate) js_macrotask_cbs: Vec<v8::Global<v8::Function>>,
- pub(crate) js_nexttick_cbs: Vec<v8::Global<v8::Function>>,
pub(crate) has_tick_scheduled: bool,
pub(crate) pending_dyn_mod_evaluate: Vec<DynImportModEvaluate>,
pub(crate) pending_mod_evaluate: Option<ModEvaluate>,
@@ -342,8 +340,6 @@ impl JsRuntime {
pending_dyn_mod_evaluate: vec![],
pending_mod_evaluate: None,
dyn_module_evaluate_idle_counter: 0,
- js_macrotask_cbs: vec![],
- js_nexttick_cbs: vec![],
has_tick_scheduled: false,
source_map_getter: options.source_map_getter,
source_map_cache: Default::default(),
@@ -541,9 +537,6 @@ impl JsRuntime {
js_runtime.init_extension_ops().unwrap();
let realm = js_runtime.global_realm();
js_runtime.init_extension_js(&realm).unwrap();
- // Init callbacks (opresolve)
- let global_realm = js_runtime.global_realm();
- js_runtime.init_cbs(&global_realm);
js_runtime
}
@@ -645,7 +638,6 @@ impl JsRuntime {
};
self.init_extension_js(&realm)?;
- self.init_cbs(&realm);
Ok(realm)
}
@@ -741,6 +733,12 @@ impl JsRuntime {
}
}
}
+
+ // TODO(bartlomieju): this not great that we need to have this conditional
+ // here, but I haven't found a better way to do it yet.
+ if ext.is_core {
+ self.init_cbs(realm);
+ }
}
// Restore extensions
self.extensions = extensions;
@@ -825,9 +823,9 @@ impl JsRuntime {
scope.escape(v).try_into().ok()
}
- /// Grabs a reference to core.js' opresolve & syncOpsCache()
+ /// Grabs a reference to core.js' eventLoopTick & buildCustomError
fn init_cbs(&mut self, realm: &JsRealm) {
- let (recv_cb, build_custom_error_cb) = {
+ let (event_loop_tick_cb, build_custom_error_cb) = {
let scope = &mut realm.handle_scope(self.v8_isolate());
let context = realm.context();
let context_local = v8::Local::new(scope, context);
@@ -836,8 +834,9 @@ impl JsRuntime {
v8::String::new_external_onebyte_static(scope, b"Deno").unwrap();
let core_str =
v8::String::new_external_onebyte_static(scope, b"core").unwrap();
- let opresolve_str =
- v8::String::new_external_onebyte_static(scope, b"opresolve").unwrap();
+ let event_loop_tick_str =
+ v8::String::new_external_onebyte_static(scope, b"eventLoopTick")
+ .unwrap();
let build_custom_error_str =
v8::String::new_external_onebyte_static(scope, b"buildCustomError")
.unwrap();
@@ -853,8 +852,8 @@ impl JsRuntime {
.try_into()
.unwrap();
- let recv_cb: v8::Local<v8::Function> = core_obj
- .get(scope, opresolve_str.into())
+ let event_loop_tick_cb: v8::Local<v8::Function> = core_obj
+ .get(scope, event_loop_tick_str.into())
.unwrap()
.try_into()
.unwrap();
@@ -864,7 +863,7 @@ impl JsRuntime {
.try_into()
.unwrap();
(
- v8::Global::new(scope, recv_cb),
+ v8::Global::new(scope, event_loop_tick_cb),
v8::Global::new(scope, build_custom_error_cb),
)
};
@@ -872,7 +871,7 @@ impl JsRuntime {
// Put global handles in the realm's ContextState
let state_rc = realm.state(self.v8_isolate());
let mut state = state_rc.borrow_mut();
- state.js_recv_cb.replace(recv_cb);
+ state.js_event_loop_tick_cb.replace(event_loop_tick_cb);
state
.js_build_custom_error_cb
.replace(build_custom_error_cb);
@@ -983,7 +982,7 @@ impl JsRuntime {
let realm = JsRealm::new(context.clone());
let realm_state_rc = realm.state(v8_isolate);
let mut realm_state = realm_state_rc.borrow_mut();
- std::mem::take(&mut realm_state.js_recv_cb);
+ std::mem::take(&mut realm_state.js_event_loop_tick_cb);
std::mem::take(&mut realm_state.js_build_custom_error_cb);
std::mem::take(&mut realm_state.js_promise_reject_cb);
std::mem::take(&mut realm_state.js_format_exception_cb);
@@ -993,8 +992,6 @@ impl JsRuntime {
}
let mut state = self.state.borrow_mut();
- state.js_macrotask_cbs.clear();
- state.js_nexttick_cbs.clear();
state.known_realms.clear();
}
@@ -1218,13 +1215,11 @@ impl JsRuntime {
}
}
- // Ops
- self.resolve_async_ops(cx)?;
- // Run all next tick callbacks and macrotasks callbacks and only then
- // check for any promise exceptions (`unhandledrejection` handlers are
- // run in macrotasks callbacks so we need to let them run first).
- self.drain_nexttick()?;
- self.drain_macrotasks()?;
+ // Resolve async ops, run all next tick callbacks and macrotasks callbacks
+ // and only then check for any promise exceptions (`unhandledrejection`
+ // handlers are run in macrotasks callbacks so we need to let them run
+ // first).
+ self.do_js_event_loop_tick(cx)?;
self.check_promise_rejections()?;
// Event loop middlewares
@@ -2191,13 +2186,13 @@ impl JsRuntime {
Ok(())
}
- // Send finished responses to JS
- fn resolve_async_ops(&mut self, cx: &mut Context) -> Result<(), Error> {
+ // Polls pending ops and then runs `Deno.core.eventLoopTick` callback.
+ fn do_js_event_loop_tick(&mut self, cx: &mut Context) -> Result<(), Error> {
// We have a specialized implementation of this method for the common case
// where there is only one realm.
let num_realms = self.state.borrow().known_realms.len();
if num_realms == 1 {
- return self.resolve_single_realm_async_ops(cx);
+ return self.do_single_realm_js_event_loop_tick(cx);
}
// `responses_per_realm[idx]` is a vector containing the promise ID and
@@ -2221,10 +2216,6 @@ impl JsRuntime {
// Handle responses for each realm.
let isolate = self.v8_isolate.as_mut().unwrap();
for (realm_idx, responses) in responses_per_realm.into_iter().enumerate() {
- if responses.is_empty() {
- continue;
- }
-
let realm = {
let context = self.state.borrow().known_realms[realm_idx]
.to_global(isolate)
@@ -2243,10 +2234,10 @@ impl JsRuntime {
// This batch is received in JS via the special `arguments` variable
// and then each tuple is used to resolve or reject promises
//
- // This can handle 16 promises (32 / 2) futures in a single batch without heap
+ // This can handle 15 promises futures in a single batch without heap
// allocations.
let mut args: SmallVec<[v8::Local<v8::Value>; 32]> =
- SmallVec::with_capacity(responses.len() * 2);
+ SmallVec::with_capacity(responses.len() * 2 + 2);
for (promise_id, mut resp) in responses {
context_state.unrefed_ops.remove(&promise_id);
@@ -2259,24 +2250,33 @@ impl JsRuntime {
});
}
- let js_recv_cb_handle = context_state.js_recv_cb.clone().unwrap();
+ let has_tick_scheduled =
+ v8::Boolean::new(scope, self.state.borrow().has_tick_scheduled);
+ args.push(has_tick_scheduled.into());
+
+ let js_event_loop_tick_cb_handle =
+ context_state.js_event_loop_tick_cb.clone().unwrap();
let tc_scope = &mut v8::TryCatch::new(scope);
- let js_recv_cb = js_recv_cb_handle.open(tc_scope);
+ let js_event_loop_tick_cb = js_event_loop_tick_cb_handle.open(tc_scope);
let this = v8::undefined(tc_scope).into();
drop(context_state);
- js_recv_cb.call(tc_scope, this, args.as_slice());
+ js_event_loop_tick_cb.call(tc_scope, this, args.as_slice());
if let Some(exception) = tc_scope.exception() {
// TODO(@andreubotella): Returning here can cause async ops in other
// realms to never resolve.
return exception_to_err_result(tc_scope, exception, false);
}
+
+ if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
+ return Ok(());
+ }
}
Ok(())
}
- fn resolve_single_realm_async_ops(
+ fn do_single_realm_js_event_loop_tick(
&mut self,
cx: &mut Context,
) -> Result<(), Error> {
@@ -2297,7 +2297,7 @@ impl JsRuntime {
// This batch is received in JS via the special `arguments` variable
// and then each tuple is used to resolve or reject promises
//
- // This can handle 16 promises (32 / 2) futures in a single batch without heap
+ // This can handle 15 promises futures in a single batch without heap
// allocations.
let mut args: SmallVec<[v8::Local<v8::Value>; 32]> = SmallVec::new();
@@ -2328,94 +2328,33 @@ impl JsRuntime {
}
}
- if args.is_empty() {
- return Ok(());
- }
+ let has_tick_scheduled =
+ v8::Boolean::new(scope, self.state.borrow().has_tick_scheduled);
+ args.push(has_tick_scheduled.into());
- let js_recv_cb_handle = {
+ let js_event_loop_tick_cb_handle = {
let state = self.state.borrow_mut();
let realm_state_rc = state.global_realm.as_ref().unwrap().state(scope);
- let handle = realm_state_rc.borrow().js_recv_cb.clone().unwrap();
+ let handle = realm_state_rc
+ .borrow()
+ .js_event_loop_tick_cb
+ .clone()
+ .unwrap();
handle
};
let tc_scope = &mut v8::TryCatch::new(scope);
- let js_recv_cb = js_recv_cb_handle.open(tc_scope);
+ let js_event_loop_tick_cb = js_event_loop_tick_cb_handle.open(tc_scope);
let this = v8::undefined(tc_scope).into();
- js_recv_cb.call(tc_scope, this, args.as_slice());
+ js_event_loop_tick_cb.call(tc_scope, this, args.as_slice());
- match tc_scope.exception() {
- None => Ok(()),
- Some(exception) => exception_to_err_result(tc_scope, exception, false),
+ if let Some(exception) = tc_scope.exception() {
+ return exception_to_err_result(tc_scope, exception, false);
}
- }
- fn drain_macrotasks(&mut self) -> Result<(), Error> {
- if self.state.borrow().js_macrotask_cbs.is_empty() {
+ if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
return Ok(());
}
- let js_macrotask_cb_handles = self.state.borrow().js_macrotask_cbs.clone();
- let scope = &mut self.handle_scope();
-
- for js_macrotask_cb_handle in js_macrotask_cb_handles {
- let js_macrotask_cb = js_macrotask_cb_handle.open(scope);
-
- // Repeatedly invoke macrotask callback until it returns true (done),
- // such that ready microtasks would be automatically run before
- // next macrotask is processed.
- let tc_scope = &mut v8::TryCatch::new(scope);
- let this = v8::undefined(tc_scope).into();
- loop {
- let is_done = js_macrotask_cb.call(tc_scope, this, &[]);
-
- if let Some(exception) = tc_scope.exception() {
- return exception_to_err_result(tc_scope, exception, false);
- }
-
- if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
- return Ok(());
- }
-
- let is_done = is_done.unwrap();
- if is_done.is_true() {
- break;
- }
- }
- }
-
- Ok(())
- }
-
- fn drain_nexttick(&mut self) -> Result<(), Error> {
- if self.state.borrow().js_nexttick_cbs.is_empty() {
- return Ok(());
- }
-
- let state = self.state.clone();
- if !state.borrow().has_tick_scheduled {
- let scope = &mut self.handle_scope();
- scope.perform_microtask_checkpoint();
- }
-
- // TODO(bartlomieju): Node also checks for absence of "rejection_to_warn"
- if !state.borrow().has_tick_scheduled {
- return Ok(());
- }
-
- let js_nexttick_cb_handles = state.borrow().js_nexttick_cbs.clone();
- let scope = &mut self.handle_scope();
-
- for js_nexttick_cb_handle in js_nexttick_cb_handles {
- let js_nexttick_cb = js_nexttick_cb_handle.open(scope);
-
- let tc_scope = &mut v8::TryCatch::new(scope);
- let this = v8::undefined(tc_scope).into();
- js_nexttick_cb.call(tc_scope, this, &[]);
- if let Some(exception) = tc_scope.exception() {
- return exception_to_err_result(tc_scope, exception, false);
- }
- }
-
Ok(())
}
}
@@ -3088,7 +3027,7 @@ pub mod tests {
.execute_script_static(
"a.js",
r#"
- Deno.core.ops.op_set_macrotask_callback(() => {
+ Deno.core.setMacrotaskCallback(() => {
return true;
});
Deno.core.ops.op_set_format_exception_callback(()=> {
@@ -3882,11 +3821,11 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
(async function () {
const results = [];
- Deno.core.ops.op_set_macrotask_callback(() => {
+ Deno.core.setMacrotaskCallback(() => {
results.push("macrotask");
return true;
});
- Deno.core.ops.op_set_next_tick_callback(() => {
+ Deno.core.setNextTickCallback(() => {
results.push("nextTick");
Deno.core.ops.op_set_has_tick_scheduled(false);
});
@@ -3905,28 +3844,6 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
runtime.run_event_loop(false).await.unwrap();
}
- #[tokio::test]
- async fn test_set_macrotask_callback_set_next_tick_callback_multiple() {
- let mut runtime = JsRuntime::new(Default::default());
-
- runtime
- .execute_script_static(
- "multiple_macrotasks_and_nextticks.js",
- r#"
- Deno.core.ops.op_set_macrotask_callback(() => { return true; });
- Deno.core.ops.op_set_macrotask_callback(() => { return true; });
- Deno.core.ops.op_set_next_tick_callback(() => {});
- Deno.core.ops.op_set_next_tick_callback(() => {});
- "#,
- )
- .unwrap();
- let isolate = runtime.v8_isolate();
- let state_rc = JsRuntime::state(isolate);
- let state = state_rc.borrow();
- assert_eq!(state.js_macrotask_cbs.len(), 2);
- assert_eq!(state.js_nexttick_cbs.len(), 2);
- }
-
#[test]
fn test_has_tick_scheduled() {
use futures::task::ArcWake;
@@ -3956,11 +3873,11 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
.execute_script_static(
"has_tick_scheduled.js",
r#"
- Deno.core.ops.op_set_macrotask_callback(() => {
+ Deno.core.setMacrotaskCallback(() => {
Deno.core.ops.op_macrotask();
return true; // We're done.
});
- Deno.core.ops.op_set_next_tick_callback(() => Deno.core.ops.op_next_tick());
+ Deno.core.setNextTickCallback(() => Deno.core.ops.op_next_tick());
Deno.core.ops.op_set_has_tick_scheduled(true);
"#,
)