summaryrefslogtreecommitdiff
path: root/core/realm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/realm.rs')
-rw-r--r--core/realm.rs198
1 files changed, 136 insertions, 62 deletions
diff --git a/core/realm.rs b/core/realm.rs
index 375f74088..fc0ff4b4b 100644
--- a/core/realm.rs
+++ b/core/realm.rs
@@ -4,8 +4,11 @@ use crate::bindings;
use crate::modules::ModuleCode;
use crate::ops::OpCtx;
use crate::runtime::exception_to_err_result;
+use crate::runtime::JsRuntimeState;
use crate::JsRuntime;
+use crate::OpCall;
use anyhow::Error;
+use futures::stream::FuturesUnordered;
use std::cell::RefCell;
use std::collections::HashSet;
use std::collections::VecDeque;
@@ -45,9 +48,11 @@ pub(crate) struct ContextState {
pub(crate) pending_promise_rejections:
VecDeque<(v8::Global<v8::Promise>, v8::Global<v8::Value>)>,
pub(crate) unrefed_ops: HashSet<i32, BuildHasherDefault<IdentityHasher>>,
+ pub(crate) pending_ops: FuturesUnordered<OpCall>,
// We don't explicitly re-read this prop but need the slice to live alongside
// the context
pub(crate) op_ctxs: Box<[OpCtx]>,
+ pub(crate) isolate: Option<*mut v8::OwnedIsolate>,
}
/// A representation of a JavaScript realm tied to a [`JsRuntime`], that allows
@@ -95,28 +100,110 @@ pub(crate) struct ContextState {
/// keep the underlying V8 context alive even if it would have otherwise been
/// garbage collected.
#[derive(Clone)]
-pub struct JsRealm(Rc<v8::Global<v8::Context>>);
-impl JsRealm {
- pub fn new(context: v8::Global<v8::Context>) -> Self {
- JsRealm(Rc::new(context))
+#[repr(transparent)]
+pub struct JsRealm(pub(crate) JsRealmInner);
+
+#[derive(Clone)]
+pub(crate) struct JsRealmInner {
+ context_state: Rc<RefCell<ContextState>>,
+ context: Rc<v8::Global<v8::Context>>,
+ runtime_state: Rc<RefCell<JsRuntimeState>>,
+ is_global: bool,
+}
+
+impl JsRealmInner {
+ pub(crate) fn new(
+ context_state: Rc<RefCell<ContextState>>,
+ context: v8::Global<v8::Context>,
+ runtime_state: Rc<RefCell<JsRuntimeState>>,
+ is_global: bool,
+ ) -> Self {
+ Self {
+ context_state,
+ context: context.into(),
+ runtime_state,
+ is_global,
+ }
+ }
+
+ pub fn num_pending_ops(&self) -> usize {
+ self.context_state.borrow().pending_ops.len()
+ }
+
+ pub fn num_unrefed_ops(&self) -> usize {
+ self.context_state.borrow().unrefed_ops.len()
}
#[inline(always)]
pub fn context(&self) -> &v8::Global<v8::Context> {
- &self.0
+ &self.context
+ }
+
+ #[inline(always)]
+ pub(crate) fn state(&self) -> Rc<RefCell<ContextState>> {
+ self.context_state.clone()
}
+ /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`].
#[inline(always)]
- pub(crate) fn state(
+ pub fn handle_scope<'s>(
&self,
- isolate: &mut v8::Isolate,
- ) -> Rc<RefCell<ContextState>> {
- self
- .context()
- .open(isolate)
- .get_slot::<Rc<RefCell<ContextState>>>(isolate)
- .unwrap()
- .clone()
+ isolate: &'s mut v8::Isolate,
+ ) -> v8::HandleScope<'s> {
+ v8::HandleScope::with_context(isolate, &*self.context)
+ }
+
+ pub(crate) fn check_promise_rejections(
+ &self,
+ scope: &mut v8::HandleScope,
+ ) -> Result<(), Error> {
+ let Some((_, handle)) = self.context_state.borrow_mut().pending_promise_rejections.pop_front() else {
+ return Ok(());
+ };
+
+ let exception = v8::Local::new(scope, handle);
+ let state_rc = JsRuntime::state(scope);
+ let state = state_rc.borrow();
+ if let Some(inspector) = &state.inspector {
+ let inspector = inspector.borrow();
+ inspector.exception_thrown(scope, exception, true);
+ if inspector.has_blocking_sessions() {
+ return Ok(());
+ }
+ }
+ exception_to_err_result(scope, exception, true)
+ }
+
+ pub(crate) fn is_same(&self, other: &Rc<v8::Global<v8::Context>>) -> bool {
+ Rc::ptr_eq(&self.context, other)
+ }
+
+ pub fn destroy(self) {
+ let state = self.state();
+ let raw_ptr = self.state().borrow().isolate.unwrap();
+ // SAFETY: We know the isolate outlives the realm
+ let isolate = unsafe { raw_ptr.as_mut().unwrap() };
+ let mut realm_state = state.borrow_mut();
+ // These globals will prevent snapshots from completing, take them
+ 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);
+ std::mem::take(&mut realm_state.js_wasm_streaming_cb);
+ // The OpCtx slice may contain a circular reference
+ std::mem::take(&mut realm_state.op_ctxs);
+
+ self.context().open(isolate).clear_all_slots(isolate);
+
+ // Expect that this context is dead (we only check this in debug mode)
+ // TODO(mmastrac): This check fails for some tests, will need to fix this
+ // debug_assert_eq!(Rc::strong_count(&self.context), 1, "Realm was still alive when we wanted to destory it. Not dropped?");
+ }
+}
+
+impl JsRealm {
+ pub(crate) fn new(inner: JsRealmInner) -> Self {
+ Self(inner)
}
#[inline(always)]
@@ -130,13 +217,28 @@ impl JsRealm {
.clone()
}
+ #[inline(always)]
+ pub fn num_pending_ops(&self) -> usize {
+ self.0.num_pending_ops()
+ }
+
+ #[inline(always)]
+ pub fn num_unrefed_ops(&self) -> usize {
+ self.0.num_unrefed_ops()
+ }
+
/// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`].
#[inline(always)]
pub fn handle_scope<'s>(
&self,
isolate: &'s mut v8::Isolate,
) -> v8::HandleScope<'s> {
- v8::HandleScope::with_context(isolate, &*self.0)
+ self.0.handle_scope(isolate)
+ }
+
+ #[inline(always)]
+ pub fn context(&self) -> &v8::Global<v8::Context> {
+ self.0.context()
}
/// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`].
@@ -144,8 +246,8 @@ impl JsRealm {
&self,
isolate: &'s mut v8::Isolate,
) -> v8::Local<'s, v8::Object> {
- let scope = &mut self.handle_scope(isolate);
- self.0.open(scope).global(scope)
+ let scope = &mut self.0.handle_scope(isolate);
+ self.0.context.open(scope).global(scope)
}
fn string_from_code<'a>(
@@ -206,7 +308,7 @@ impl JsRealm {
name: &'static str,
source_code: ModuleCode,
) -> Result<v8::Global<v8::Value>, Error> {
- let scope = &mut self.handle_scope(isolate);
+ let scope = &mut self.0.handle_scope(isolate);
let source = Self::string_from_code(scope, &source_code).unwrap();
debug_assert!(name.is_ascii());
@@ -240,51 +342,23 @@ impl JsRealm {
// TODO(andreubotella): `mod_evaluate`, `load_main_module`, `load_side_module`
}
-pub struct JsRealmLocal<'s>(v8::Local<'s, v8::Context>);
-impl<'s> JsRealmLocal<'s> {
- pub fn new(context: v8::Local<'s, v8::Context>) -> Self {
- JsRealmLocal(context)
- }
-
- #[inline(always)]
- pub fn context(&self) -> v8::Local<v8::Context> {
- self.0
- }
-
- #[inline(always)]
- pub(crate) fn state(
- &self,
- isolate: &mut v8::Isolate,
- ) -> Rc<RefCell<ContextState>> {
- self
- .context()
- .get_slot::<Rc<RefCell<ContextState>>>(isolate)
- .unwrap()
- .clone()
- }
-
- pub(crate) fn check_promise_rejections(
- &self,
- scope: &mut v8::HandleScope,
- ) -> Result<(), Error> {
- let context_state_rc = self.state(scope);
- let mut context_state = context_state_rc.borrow_mut();
-
- let Some((_, handle)) = context_state.pending_promise_rejections.pop_front() else {
- return Ok(());
- };
- drop(context_state);
+impl Drop for JsRealm {
+ fn drop(&mut self) {
+ // Don't do anything special with the global realm
+ if self.0.is_global {
+ return;
+ }
- let exception = v8::Local::new(scope, handle);
- let state_rc = JsRuntime::state(scope);
- let state = state_rc.borrow();
- if let Some(inspector) = &state.inspector {
- let inspector = inspector.borrow();
- inspector.exception_thrown(scope, exception, true);
- if inspector.has_blocking_sessions() {
- return Ok(());
- }
+ // There's us and there's the runtime
+ if Rc::strong_count(&self.0.context) == 2 {
+ self
+ .0
+ .runtime_state
+ .borrow_mut()
+ .remove_realm(&self.0.context);
+ assert_eq!(Rc::strong_count(&self.0.context), 1);
+ self.0.clone().destroy();
+ assert_eq!(Rc::strong_count(&self.0.context_state), 1);
}
- exception_to_err_result(scope, exception, true)
}
}