summaryrefslogtreecommitdiff
path: root/core/runtime.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/runtime.rs')
-rw-r--r--core/runtime.rs216
1 files changed, 124 insertions, 92 deletions
diff --git a/core/runtime.rs b/core/runtime.rs
index c1ef6d1a4..3ccff9af7 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -77,6 +77,7 @@ struct IsolateAllocations {
/// by implementing an async function that takes a serde::Deserialize "control argument"
/// and an optional zero copy buffer, each async Op is tied to a Promise in JavaScript.
pub struct JsRuntime {
+ state: Rc<RefCell<JsRuntimeState>>,
// This is an Option<OwnedIsolate> instead of just OwnedIsolate to workaround
// a safety issue with SnapshotCreator. See JsRuntime::drop.
v8_isolate: Option<v8::OwnedIsolate>,
@@ -144,7 +145,7 @@ pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
/// Internal state for JsRuntime which is stored in one of v8::Isolate's
/// embedder slots.
-pub(crate) struct JsRuntimeState {
+pub struct JsRuntimeState {
global_realm: Option<JsRealm>,
pub(crate) js_recv_cb: Option<v8::Global<v8::Function>>,
pub(crate) js_macrotask_cbs: Vec<v8::Global<v8::Function>>,
@@ -308,12 +309,55 @@ impl JsRuntime {
op_state.get_error_class_fn = get_error_class_fn;
}
let op_state = Rc::new(RefCell::new(op_state));
+
+ let align = std::mem::align_of::<usize>();
+ let layout = std::alloc::Layout::from_size_align(
+ std::mem::size_of::<*mut v8::OwnedIsolate>(),
+ align,
+ )
+ .unwrap();
+ assert!(layout.size() > 0);
+ let isolate_ptr: *mut v8::OwnedIsolate =
+ // SAFETY: we just asserted that layout has non-0 size.
+ unsafe { std::alloc::alloc(layout) as *mut _ };
+
+ let state_rc = Rc::new(RefCell::new(JsRuntimeState {
+ pending_promise_exceptions: HashMap::new(),
+ pending_dyn_mod_evaluate: vec![],
+ pending_mod_evaluate: None,
+ dyn_module_evaluate_idle_counter: 0,
+ js_recv_cb: None,
+ js_macrotask_cbs: vec![],
+ js_nexttick_cbs: vec![],
+ js_promise_reject_cb: None,
+ js_format_exception_cb: None,
+ js_build_custom_error_cb: None,
+ has_tick_scheduled: false,
+ js_wasm_streaming_cb: None,
+ source_map_getter: options.source_map_getter,
+ source_map_cache: Default::default(),
+ pending_ops: FuturesUnordered::new(),
+ unrefed_ops: HashSet::new(),
+ shared_array_buffer_store: options.shared_array_buffer_store,
+ compiled_wasm_module_store: options.compiled_wasm_module_store,
+ op_state: op_state.clone(),
+ waker: AtomicWaker::new(),
+ have_unpolled_ops: false,
+ dispatched_exceptions: Default::default(),
+ // Some fields are initialized later after isolate is created
+ inspector: None,
+ op_ctxs: vec![].into_boxed_slice(),
+ global_realm: None,
+ }));
+
+ let weak = Rc::downgrade(&state_rc);
let op_ctxs = ops
.into_iter()
.enumerate()
.map(|(id, decl)| OpCtx {
id,
state: op_state.clone(),
+ runtime_state: weak.clone(),
decl,
})
.collect::<Vec<_>>()
@@ -324,17 +368,6 @@ impl JsRuntime {
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
let global_context;
- let align = std::mem::align_of::<usize>();
- let layout = std::alloc::Layout::from_size_align(
- std::mem::size_of::<*mut v8::OwnedIsolate>(),
- align,
- )
- .unwrap();
- assert!(layout.size() > 0);
- let isolate_ptr: *mut v8::OwnedIsolate =
- // SAFETY: we just asserted that layout has non-0 size.
- unsafe { std::alloc::alloc(layout) as *mut _ };
-
let mut isolate = if options.will_snapshot {
// TODO(ry) Support loading snapshots before snapshotting.
assert!(options.startup_snapshot.is_none());
@@ -401,37 +434,15 @@ impl JsRuntime {
let loader = options
.module_loader
.unwrap_or_else(|| Rc::new(NoopModuleLoader));
-
- let state_rc = Rc::new(RefCell::new(JsRuntimeState {
- global_realm: Some(JsRealm(global_context)),
- pending_promise_exceptions: HashMap::new(),
- pending_dyn_mod_evaluate: vec![],
- pending_mod_evaluate: None,
- dyn_module_evaluate_idle_counter: 0,
- js_recv_cb: None,
- js_macrotask_cbs: vec![],
- js_nexttick_cbs: vec![],
- js_promise_reject_cb: None,
- js_format_exception_cb: None,
- js_build_custom_error_cb: None,
- has_tick_scheduled: false,
- js_wasm_streaming_cb: None,
- source_map_getter: options.source_map_getter,
- source_map_cache: Default::default(),
- pending_ops: FuturesUnordered::new(),
- unrefed_ops: HashSet::new(),
- shared_array_buffer_store: options.shared_array_buffer_store,
- compiled_wasm_module_store: options.compiled_wasm_module_store,
- op_state: op_state.clone(),
- op_ctxs,
- have_unpolled_ops: false,
- dispatched_exceptions: Default::default(),
- inspector: Some(inspector),
- waker: AtomicWaker::new(),
- }));
+ {
+ let mut state = state_rc.borrow_mut();
+ state.global_realm = Some(JsRealm(global_context));
+ state.op_ctxs = op_ctxs;
+ state.inspector = Some(inspector);
+ }
isolate.set_data(
Self::STATE_DATA_OFFSET,
- Rc::into_raw(state_rc) as *mut c_void,
+ Rc::into_raw(state_rc.clone()) as *mut c_void,
);
let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader, op_state)));
@@ -446,6 +457,7 @@ impl JsRuntime {
allocations: IsolateAllocations::default(),
event_loop_middlewares: Vec::with_capacity(options.extensions.len()),
extensions: options.extensions,
+ state: state_rc,
};
// Init resources and ops before extensions to make sure they are
@@ -479,21 +491,24 @@ impl JsRuntime {
drop(module_map_rc);
}
+ #[inline]
pub fn global_context(&mut self) -> v8::Global<v8::Context> {
self.global_realm().0
}
+ #[inline]
pub fn v8_isolate(&mut self) -> &mut v8::OwnedIsolate {
self.v8_isolate.as_mut().unwrap()
}
+ #[inline]
pub fn inspector(&mut self) -> Rc<RefCell<JsRuntimeInspector>> {
- Self::state(self.v8_isolate()).borrow().inspector()
+ self.state.borrow().inspector()
}
+ #[inline]
pub fn global_realm(&mut self) -> JsRealm {
- let state = Self::state(self.v8_isolate());
- let state = state.borrow();
+ let state = self.state.borrow();
state.global_realm.clone().unwrap()
}
@@ -518,7 +533,7 @@ impl JsRuntime {
});
let context = bindings::initialize_context(
scope,
- &Self::state(self.v8_isolate()).borrow().op_ctxs,
+ &self.state.borrow().op_ctxs,
self.built_from_snapshot,
);
JsRealm::new(v8::Global::new(scope, context))
@@ -530,6 +545,7 @@ impl JsRuntime {
Ok(realm)
}
+ #[inline]
pub fn handle_scope(&mut self) -> v8::HandleScope {
self.global_realm().handle_scope(self.v8_isolate())
}
@@ -716,8 +732,7 @@ impl JsRuntime {
/// Returns the runtime's op state, which can be used to maintain ops
/// and access resources between op calls.
pub fn op_state(&mut self) -> Rc<RefCell<OpState>> {
- let state_rc = Self::state(self.v8_isolate());
- let state = state_rc.borrow();
+ let state = self.state.borrow();
state.op_state.clone()
}
@@ -762,11 +777,8 @@ impl JsRuntime {
}
}
- let state = Self::state(self.v8_isolate());
-
- state.borrow_mut().global_realm.take();
-
- state.borrow_mut().inspector.take();
+ self.state.borrow_mut().global_realm.take();
+ self.state.borrow_mut().inspector.take();
// Drop existing ModuleMap to drop v8::Global handles
{
@@ -775,7 +787,7 @@ impl JsRuntime {
}
// Drop other v8::Global handles before snapshotting
{
- let mut state = state.borrow_mut();
+ let mut state = self.state.borrow_mut();
std::mem::take(&mut state.js_recv_cb);
std::mem::take(&mut state.js_promise_reject_cb);
std::mem::take(&mut state.js_format_exception_cb);
@@ -952,10 +964,9 @@ impl JsRuntime {
) -> Poll<Result<(), Error>> {
// We always poll the inspector first
let _ = self.inspector().borrow_mut().poll_unpin(cx);
- let state_rc = Self::state(self.v8_isolate());
let module_map_rc = Self::module_map(self.v8_isolate());
{
- let state = state_rc.borrow();
+ let state = self.state.borrow();
state.waker.register(cx.waker());
}
@@ -999,7 +1010,7 @@ impl JsRuntime {
// Event loop middlewares
let mut maybe_scheduling = false;
{
- let op_state = state_rc.borrow().op_state.clone();
+ let op_state = self.state.borrow().op_state.clone();
for f in &self.event_loop_middlewares {
if f(op_state.clone(), cx) {
maybe_scheduling = true;
@@ -1010,11 +1021,10 @@ impl JsRuntime {
// Top level module
self.evaluate_pending_module();
- let pending_state = Self::event_loop_pending_state(self.v8_isolate());
- let inspector_has_active_sessions =
- self.inspector().borrow_mut().has_active_sessions();
-
+ let pending_state = self.event_loop_pending_state();
if !pending_state.is_pending() && !maybe_scheduling {
+ let inspector_has_active_sessions =
+ self.inspector().borrow_mut().has_active_sessions();
if wait_for_inspector && inspector_has_active_sessions {
return Poll::Pending;
}
@@ -1022,7 +1032,7 @@ impl JsRuntime {
return Poll::Ready(Ok(()));
}
- let mut state = state_rc.borrow_mut();
+ let mut state = self.state.borrow_mut();
let module_map = module_map_rc.borrow();
// Check if more async ops have been dispatched
@@ -1085,7 +1095,25 @@ Pending dynamic modules:\n".to_string();
Poll::Pending
}
- pub(crate) fn event_loop_pending_state(
+ fn event_loop_pending_state(&mut self) -> EventLoopPendingState {
+ let isolate = self.v8_isolate.as_mut().unwrap();
+ let module_map_rc = Self::module_map(isolate);
+ let state = self.state.borrow_mut();
+ let module_map = module_map_rc.borrow();
+
+ EventLoopPendingState {
+ has_pending_refed_ops: state.pending_ops.len() > state.unrefed_ops.len(),
+ has_pending_dyn_imports: module_map.has_pending_dynamic_imports(),
+ has_pending_dyn_module_evaluation: !state
+ .pending_dyn_mod_evaluate
+ .is_empty(),
+ has_pending_module_evaluation: state.pending_mod_evaluate.is_some(),
+ has_pending_background_tasks: isolate.has_pending_background_tasks(),
+ has_tick_scheduled: state.has_tick_scheduled,
+ }
+ }
+
+ pub(crate) fn event_loop_pending_state_from_isolate(
isolate: &mut v8::Isolate,
) -> EventLoopPendingState {
let state_rc = Self::state(isolate);
@@ -1242,7 +1270,6 @@ impl JsRuntime {
load_id: ModuleLoadId,
id: ModuleId,
) -> Result<(), Error> {
- let state_rc = Self::state(self.v8_isolate());
let module_map_rc = Self::module_map(self.v8_isolate());
let module_handle = module_map_rc
@@ -1272,7 +1299,9 @@ impl JsRuntime {
// For more details see:
// https://github.com/denoland/deno/issues/4908
// https://v8.dev/features/top-level-await#module-execution-order
- let scope = &mut self.handle_scope();
+ let global_realm = self.state.borrow_mut().global_realm.clone().unwrap();
+ let scope =
+ &mut global_realm.handle_scope(self.v8_isolate.as_mut().unwrap());
let tc_scope = &mut v8::TryCatch::new(scope);
let module = v8::Local::new(tc_scope, &module_handle);
let maybe_value = module.evaluate(tc_scope);
@@ -1289,7 +1318,6 @@ impl JsRuntime {
.expect("Expected to get promise as module evaluation result");
let empty_fn = bindings::create_empty_fn(tc_scope).unwrap();
promise.catch(tc_scope, empty_fn);
- let mut state = state_rc.borrow_mut();
let promise_global = v8::Global::new(tc_scope, promise);
let module_global = v8::Global::new(tc_scope, module);
@@ -1300,7 +1328,11 @@ impl JsRuntime {
module: module_global,
};
- state.pending_dyn_mod_evaluate.push(dyn_import_mod_evaluate);
+ self
+ .state
+ .borrow_mut()
+ .pending_dyn_mod_evaluate
+ .push(dyn_import_mod_evaluate);
} else if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
return Err(
generic_error("Cannot evaluate dynamically imported module, because JavaScript execution has been terminated.")
@@ -1327,7 +1359,7 @@ impl JsRuntime {
&mut self,
id: ModuleId,
) -> oneshot::Receiver<Result<(), Error>> {
- let state_rc = Self::state(self.v8_isolate());
+ let state_rc = self.state.clone();
let module_map_rc = Self::module_map(self.v8_isolate());
let scope = &mut self.handle_scope();
let tc_scope = &mut v8::TryCatch::new(scope);
@@ -1457,7 +1489,7 @@ impl JsRuntime {
}
fn dynamic_import_resolve(&mut self, id: ModuleLoadId, mod_id: ModuleId) {
- let state_rc = Self::state(self.v8_isolate());
+ let state_rc = self.state.clone();
let module_map_rc = Self::module_map(self.v8_isolate());
let scope = &mut self.handle_scope();
@@ -1621,7 +1653,7 @@ impl JsRuntime {
/// resolved or rejected the promise. If the promise is still pending
/// then another turn of event loop must be performed.
fn evaluate_pending_module(&mut self) {
- let state_rc = Self::state(self.v8_isolate());
+ let state_rc = self.state.clone();
let maybe_module_evaluation =
state_rc.borrow_mut().pending_mod_evaluate.take();
@@ -1672,10 +1704,9 @@ impl JsRuntime {
// Returns true if some dynamic import was resolved.
fn evaluate_dyn_imports(&mut self) -> bool {
let mut resolved_any = false;
- let state_rc = Self::state(self.v8_isolate());
let mut still_pending = vec![];
let pending =
- std::mem::take(&mut state_rc.borrow_mut().pending_dyn_mod_evaluate);
+ std::mem::take(&mut self.state.borrow_mut().pending_dyn_mod_evaluate);
for pending_dyn_evaluate in pending {
let maybe_result = {
let scope = &mut self.handle_scope();
@@ -1713,7 +1744,7 @@ impl JsRuntime {
}
}
}
- state_rc.borrow_mut().pending_dyn_mod_evaluate = still_pending;
+ self.state.borrow_mut().pending_dyn_mod_evaluate = still_pending;
resolved_any
}
@@ -1836,8 +1867,7 @@ impl JsRuntime {
}
fn check_promise_exceptions(&mut self) -> Result<(), Error> {
- let state_rc = Self::state(self.v8_isolate());
- let mut state = state_rc.borrow_mut();
+ let mut state = self.state.borrow_mut();
if state.pending_promise_exceptions.is_empty() {
return Ok(());
@@ -1861,10 +1891,11 @@ impl JsRuntime {
// Send finished responses to JS
fn resolve_async_ops(&mut self, cx: &mut Context) -> Result<(), Error> {
- let state_rc = Self::state(self.v8_isolate());
+ let isolate = self.v8_isolate.as_mut().unwrap();
- let js_recv_cb_handle = state_rc.borrow().js_recv_cb.clone().unwrap();
- let scope = &mut self.handle_scope();
+ let js_recv_cb_handle = self.state.borrow().js_recv_cb.clone().unwrap();
+ let global_realm = self.state.borrow().global_realm.clone().unwrap();
+ let scope = &mut global_realm.handle_scope(isolate);
// We return async responses to JS in unbounded batches (may change),
// each batch is a flat vector of tuples:
@@ -1877,7 +1908,7 @@ impl JsRuntime {
// Now handle actual ops.
{
- let mut state = state_rc.borrow_mut();
+ let mut state = self.state.borrow_mut();
state.have_unpolled_ops = false;
while let Poll::Ready(Some(item)) = state.pending_ops.poll_next_unpin(cx)
@@ -1911,13 +1942,11 @@ impl JsRuntime {
}
fn drain_macrotasks(&mut self) -> Result<(), Error> {
- let state = Self::state(self.v8_isolate());
-
- if state.borrow().js_macrotask_cbs.is_empty() {
+ if self.state.borrow().js_macrotask_cbs.is_empty() {
return Ok(());
}
- let js_macrotask_cb_handles = state.borrow().js_macrotask_cbs.clone();
+ 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 {
@@ -1950,12 +1979,11 @@ impl JsRuntime {
}
fn drain_nexttick(&mut self) -> Result<(), Error> {
- let state = Self::state(self.v8_isolate());
-
- if state.borrow().js_nexttick_cbs.is_empty() {
+ 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();
@@ -2088,11 +2116,17 @@ impl JsRealm {
#[inline]
pub fn queue_async_op(
- state: Rc<RefCell<OpState>>,
+ ctx: &OpCtx,
scope: &mut v8::HandleScope,
deferred: bool,
op: impl Future<Output = (PromiseId, OpId, OpResult)> + 'static,
) {
+ let runtime_state = match ctx.runtime_state.upgrade() {
+ Some(rc_state) => rc_state,
+ // atleast 1 Rc is held by the JsRuntime.
+ None => unreachable!(),
+ };
+
match OpCall::eager(op) {
// This calls promise.resolve() before the control goes back to userland JS. It works something
// along the lines of:
@@ -2110,8 +2144,8 @@ pub fn queue_async_op(
];
let js_recv_cb_handle =
- JsRuntime::state(scope).borrow().js_recv_cb.clone().unwrap();
- state.borrow().tracker.track_async_completed(op_id);
+ runtime_state.borrow().js_recv_cb.clone().unwrap();
+ ctx.state.borrow_mut().tracker.track_async_completed(op_id);
let tc_scope = &mut v8::TryCatch::new(scope);
let js_recv_cb = js_recv_cb_handle.open(tc_scope);
@@ -2120,14 +2154,12 @@ pub fn queue_async_op(
}
EagerPollResult::Ready(op) => {
let ready = OpCall::ready(op);
- let state_rc = JsRuntime::state(scope);
- let mut state = state_rc.borrow_mut();
+ let mut state = runtime_state.borrow_mut();
state.pending_ops.push(ready);
state.have_unpolled_ops = true;
}
EagerPollResult::Pending(op) => {
- let state_rc = JsRuntime::state(scope);
- let mut state = state_rc.borrow_mut();
+ let mut state = runtime_state.borrow_mut();
state.pending_ops.push(op);
state.have_unpolled_ops = true;
}