Skip to content

Commit 8802459

Browse files
committed
Store IsolateHandle data in its own allocation
1 parent 5f7713c commit 8802459

File tree

2 files changed

+131
-87
lines changed

2 files changed

+131
-87
lines changed

src/isolate.rs

Lines changed: 130 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use crate::support::int;
4343
use crate::support::size_t;
4444
use crate::wasm::WasmStreaming;
4545
use crate::wasm::trampoline;
46+
use std::cell::UnsafeCell;
4647
use std::ffi::CStr;
4748

4849
use std::any::Any;
@@ -583,12 +584,6 @@ pub type PrepareStackTraceCallback<'s> =
583584
Local<'s, Array>,
584585
) -> *mut *const Value;
585586

586-
// System V ABI: MaybeLocal<Value> returned in a register.
587-
// System V i386 ABI: Local<Value> returned in hidden pointer (struct).
588-
#[cfg(not(target_os = "windows"))]
589-
#[repr(C)]
590-
pub struct PrepareStackTraceCallbackRet(*const Value);
591-
592587
#[cfg(not(target_os = "windows"))]
593588
pub type PrepareStackTraceCallback<'s> =
594589
unsafe extern "C" fn(
@@ -597,6 +592,12 @@ pub type PrepareStackTraceCallback<'s> =
597592
Local<'s, Array>,
598593
) -> PrepareStackTraceCallbackRet;
599594

595+
// System V ABI: MaybeLocal<Value> returned in a register.
596+
// System V i386 ABI: Local<Value> returned in hidden pointer (struct).
597+
#[cfg(not(target_os = "windows"))]
598+
#[repr(C)]
599+
pub struct PrepareStackTraceCallbackRet(*const Value);
600+
600601
pub type UseCounterFeature = v8__Isolate__UseCounterFeature;
601602
pub type UseCounterCallback =
602603
unsafe extern "C" fn(&mut Isolate, UseCounterFeature);
@@ -890,7 +891,7 @@ impl Isolate {
890891

891892
// Isolate data slots used internally by rusty_v8.
892893
const ANNEX_SLOT: u32 = 0;
893-
const INTERNAL_DATA_SLOT_COUNT: u32 = 2;
894+
const INTERNAL_DATA_SLOT_COUNT: u32 = 1;
894895

895896
#[inline(always)]
896897
fn assert_embedder_data_slot_count_and_offset_correct(&self) {
@@ -955,7 +956,7 @@ impl Isolate {
955956

956957
#[inline(always)]
957958
pub fn thread_safe_handle(&self) -> IsolateHandle {
958-
IsolateHandle::new(self)
959+
self.get_annex().isolate_handle.clone()
959960
}
960961

961962
/// See [`IsolateHandle::terminate_execution`]
@@ -980,25 +981,23 @@ impl Isolate {
980981
&mut self,
981982
create_param_allocations: Box<dyn Any>,
982983
) {
983-
let annex_arc = Arc::new(IsolateAnnex::new(self, create_param_allocations));
984-
let annex_ptr = Arc::into_raw(annex_arc);
984+
let annex_box = Box::new(IsolateAnnex::new(self, create_param_allocations));
985+
let annex_ptr = Box::into_raw(annex_box);
985986
assert!(self.get_data_internal(Self::ANNEX_SLOT).is_null());
986987
self.set_data_internal(Self::ANNEX_SLOT, annex_ptr as *mut _);
987988
}
988989

989990
unsafe fn dispose_annex(&mut self) -> Box<dyn Any> {
991+
// SAFETY: `dispose_annex()` is only called once, when the `Isolate` is dropped.
992+
let mut annex = unsafe { self.take_annex() };
993+
990994
// Set the `isolate` pointer inside the annex struct to null, so any
991995
// IsolateHandle that outlives the isolate will know that it can't call
992996
// methods on the isolate.
993-
let annex = self.get_annex_mut();
994-
{
995-
let _lock = annex.isolate_mutex.lock().unwrap();
996-
annex.isolate = null_mut();
997-
}
997+
annex.isolate_handle.dispose();
998998

999999
// Clear slots and drop owned objects that were taken out of `CreateParams`.
1000-
let create_param_allocations =
1001-
std::mem::replace(&mut annex.create_param_allocations, Box::new(()));
1000+
let create_param_allocations = annex.create_param_allocations;
10021001
annex.slots.clear();
10031002

10041003
// Run through any remaining guaranteed finalizers.
@@ -1008,10 +1007,6 @@ impl Isolate {
10081007
}
10091008
}
10101009

1011-
// Subtract one from the Arc<IsolateAnnex> reference count.
1012-
unsafe { Arc::from_raw(annex) };
1013-
self.set_data(0, null_mut());
1014-
10151010
create_param_allocations
10161011
}
10171012

@@ -1031,6 +1026,15 @@ impl Isolate {
10311026
unsafe { &mut *annex_ptr }
10321027
}
10331028

1029+
/// # Safety
1030+
/// This must only be called once.
1031+
#[inline(always)]
1032+
unsafe fn take_annex(&mut self) -> Box<IsolateAnnex> {
1033+
let annex_ptr =
1034+
self.take_data_internal(Self::ANNEX_SLOT) as *mut IsolateAnnex;
1035+
unsafe { Box::from_raw(annex_ptr) }
1036+
}
1037+
10341038
pub(crate) fn set_snapshot_creator(
10351039
&mut self,
10361040
snapshot_creator: SnapshotCreator,
@@ -1050,13 +1054,6 @@ impl Isolate {
10501054
&mut self.get_annex_mut().finalizer_map
10511055
}
10521056

1053-
fn get_annex_arc(&self) -> Arc<IsolateAnnex> {
1054-
let annex_ptr = self.get_annex();
1055-
let annex_arc = unsafe { Arc::from_raw(annex_ptr) };
1056-
let _ = Arc::into_raw(annex_arc.clone());
1057-
annex_arc
1058-
}
1059-
10601057
/// Retrieve embedder-specific data from the isolate.
10611058
/// Returns NULL if SetData has never been called for the given `slot`.
10621059
pub fn get_data(&self, slot: u32) -> *mut c_void {
@@ -1087,6 +1084,14 @@ impl Isolate {
10871084
unsafe { v8__Isolate__SetData(self.as_real_ptr(), slot, data) }
10881085
}
10891086

1087+
/// Get the value of the slot and replace it with a null pointer.
1088+
#[inline(always)]
1089+
fn take_data_internal(&mut self, slot: u32) -> *mut c_void {
1090+
let ptr = self.get_data_internal(slot);
1091+
self.set_data_internal(slot, null_mut());
1092+
ptr
1093+
}
1094+
10901095
// pub(crate) fn init_scope_root(&mut self) {
10911096
// ScopeData::new_root(self);
10921097
// }
@@ -1876,65 +1881,111 @@ pub(crate) struct IsolateAnnex {
18761881
slots: HashMap<TypeId, RawSlot, BuildTypeIdHasher>,
18771882
finalizer_map: FinalizerMap,
18781883
maybe_snapshot_creator: Option<SnapshotCreator>,
1879-
// The `isolate` and `isolate_mutex` fields are there so an `IsolateHandle`
1880-
// (which may outlive the isolate itself) can determine whether the isolate
1881-
// is still alive, and if so, get a reference to it. Safety rules:
1882-
// - The 'main thread' must lock the mutex and reset `isolate` to null just
1883-
// before the isolate is disposed.
1884-
// - Any other thread must lock the mutex while it's reading/using the
1885-
// `isolate` pointer.
1886-
isolate: *mut RealIsolate,
1887-
isolate_mutex: Mutex<()>,
1884+
isolate_handle: IsolateHandle,
18881885
}
18891886

1890-
unsafe impl Send for IsolateAnnex {}
1891-
unsafe impl Sync for IsolateAnnex {}
1892-
18931887
impl IsolateAnnex {
1894-
fn new(
1895-
isolate: &mut Isolate,
1896-
create_param_allocations: Box<dyn Any>,
1897-
) -> Self {
1888+
fn new(isolate: &Isolate, create_param_allocations: Box<dyn Any>) -> Self {
18981889
Self {
18991890
create_param_allocations,
19001891
slots: HashMap::default(),
19011892
finalizer_map: FinalizerMap::default(),
19021893
maybe_snapshot_creator: None,
1903-
isolate: isolate.as_real_ptr(),
1904-
isolate_mutex: Mutex::new(()),
1894+
isolate_handle: IsolateHandle::new(isolate),
19051895
}
19061896
}
19071897
}
19081898

19091899
impl Debug for IsolateAnnex {
19101900
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
19111901
f.debug_struct("IsolateAnnex")
1912-
.field("isolate", &self.isolate)
1913-
.field("isolate_mutex", &self.isolate_mutex)
1902+
.field("isolate_handle", &self.isolate_handle)
19141903
.finish()
19151904
}
19161905
}
19171906

1918-
/// IsolateHandle is a thread-safe reference to an Isolate. It's main use is to
1907+
pub(crate) struct IsolateHandleInner {
1908+
/// Safety invariants:
1909+
/// - The 'main thread' must lock the mutex and reset `isolate` to null just
1910+
/// before the isolate is disposed.
1911+
/// - Any other thread must lock the mutex while it's reading/using the
1912+
/// `isolate` pointer.
1913+
// These two fields can be replaced with a `Mutex<*mut RealIsolate>` once
1914+
// `Mutex::data_ptr()` is stabilized.
1915+
isolate: UnsafeCell<*mut RealIsolate>,
1916+
isolate_mutex: Mutex<()>,
1917+
}
1918+
1919+
unsafe impl Send for IsolateHandleInner {}
1920+
unsafe impl Sync for IsolateHandleInner {}
1921+
1922+
/// IsolateHandle is a thread-safe reference to an Isolate. Its main use is to
19191923
/// terminate execution of a running isolate from another thread.
19201924
///
1921-
/// It is created with Isolate::thread_safe_handle().
1925+
/// It is created with [`Isolate::thread_safe_handle()`].
19221926
///
19231927
/// IsolateHandle is Cloneable, Send, and Sync.
1924-
#[derive(Clone, Debug)]
1925-
pub struct IsolateHandle(Arc<IsolateAnnex>);
1928+
#[derive(Clone)]
1929+
pub struct IsolateHandle(Arc<IsolateHandleInner>);
19261930

1927-
impl IsolateHandle {
1928-
// This function is marked unsafe because it must be called only with either
1929-
// IsolateAnnex::mutex locked, or from the main thread associated with the V8
1930-
// isolate.
1931-
pub(crate) unsafe fn get_isolate_ptr(&self) -> *mut RealIsolate {
1932-
self.0.isolate
1931+
impl fmt::Debug for IsolateHandle {
1932+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1933+
if let Ok(_lock) = self.0.isolate_mutex.try_lock() {
1934+
// SAFETY: mutex lock is held
1935+
let ptr = unsafe { *self.0.isolate.get() };
1936+
f.debug_struct("IsolateHandle")
1937+
.field("isolate_ptr", &ptr)
1938+
.finish()
1939+
} else {
1940+
f.debug_struct("IsolateHandle").finish_non_exhaustive()
1941+
}
19331942
}
1943+
}
19341944

1945+
impl IsolateHandle {
19351946
#[inline(always)]
19361947
fn new(isolate: &Isolate) -> Self {
1937-
Self(isolate.get_annex_arc())
1948+
let inner = Arc::new(IsolateHandleInner {
1949+
isolate: UnsafeCell::new(isolate.as_real_ptr()),
1950+
isolate_mutex: Mutex::new(()),
1951+
});
1952+
Self(inner)
1953+
}
1954+
1955+
/// Set the inner isolate pointer to null.
1956+
fn dispose(self) {
1957+
let _lock = self.0.isolate_mutex.lock().unwrap();
1958+
// SAFETY: mutex lock is held
1959+
unsafe { *self.0.isolate.get() = null_mut() }
1960+
}
1961+
1962+
/// Access the isolate, if it hasn't yet been disposed of.
1963+
///
1964+
/// A lock is taken on the pointer and held for the scope of `f`, which
1965+
/// means the isolate can't be dropped until after `f` returns. If you
1966+
/// do something with the isolate afterwards, that needs to be verified
1967+
/// to be safe separately.
1968+
pub(crate) fn with_isolate_ptr<R>(
1969+
&self,
1970+
f: impl FnOnce(NonNull<RealIsolate>) -> R,
1971+
) -> Option<R> {
1972+
let _lock = self.0.isolate_mutex.lock().unwrap();
1973+
// SAFETY: mutex lock is held
1974+
let ptr = unsafe { *self.0.isolate.get() };
1975+
NonNull::new(ptr).map(f)
1976+
}
1977+
1978+
/// Access the pointer for this isolate - it may be null.
1979+
///
1980+
/// # Safety
1981+
/// This function must only be called from the main thread associated with
1982+
/// the V8 isolate.
1983+
// TODO: have this return an `Option<NonNull<RealIsolate>>`
1984+
pub(crate) unsafe fn get_isolate_ptr(&self) -> *mut RealIsolate {
1985+
// SAFETY: this function must only be called from the main thread of the
1986+
// isolate, which means that `Isolate::dispose_annex` can't concurrently
1987+
// be setting this to null.
1988+
unsafe { *self.0.isolate.get() }
19381989
}
19391990

19401991
/// Forcefully terminate the current thread of JavaScript execution
@@ -1946,13 +1997,11 @@ impl IsolateHandle {
19461997
/// Returns false if Isolate was already destroyed.
19471998
#[inline(always)]
19481999
pub fn terminate_execution(&self) -> bool {
1949-
let _lock = self.0.isolate_mutex.lock().unwrap();
1950-
if self.0.isolate.is_null() {
1951-
false
1952-
} else {
1953-
unsafe { v8__Isolate__TerminateExecution(self.0.isolate) };
1954-
true
1955-
}
2000+
self
2001+
.with_isolate_ptr(|isolate| unsafe {
2002+
v8__Isolate__TerminateExecution(isolate.as_ptr())
2003+
})
2004+
.is_some()
19562005
}
19572006

19582007
/// Resume execution capability in the given isolate, whose execution
@@ -1971,13 +2020,11 @@ impl IsolateHandle {
19712020
/// Returns false if Isolate was already destroyed.
19722021
#[inline(always)]
19732022
pub fn cancel_terminate_execution(&self) -> bool {
1974-
let _lock = self.0.isolate_mutex.lock().unwrap();
1975-
if self.0.isolate.is_null() {
1976-
false
1977-
} else {
1978-
unsafe { v8__Isolate__CancelTerminateExecution(self.0.isolate) };
1979-
true
1980-
}
2023+
self
2024+
.with_isolate_ptr(|isolate| unsafe {
2025+
v8__Isolate__CancelTerminateExecution(isolate.as_ptr())
2026+
})
2027+
.is_some()
19812028
}
19822029

19832030
/// Is V8 terminating JavaScript execution.
@@ -1990,12 +2037,11 @@ impl IsolateHandle {
19902037
/// Returns false if Isolate was already destroyed.
19912038
#[inline(always)]
19922039
pub fn is_execution_terminating(&self) -> bool {
1993-
let _lock = self.0.isolate_mutex.lock().unwrap();
1994-
if self.0.isolate.is_null() {
1995-
false
1996-
} else {
1997-
unsafe { v8__Isolate__IsExecutionTerminating(self.0.isolate) }
1998-
}
2040+
self
2041+
.with_isolate_ptr(|isolate| unsafe {
2042+
v8__Isolate__IsExecutionTerminating(isolate.as_ptr())
2043+
})
2044+
.unwrap_or(false)
19992045
}
20002046

20012047
/// Request V8 to interrupt long running JavaScript code and invoke
@@ -2015,13 +2061,11 @@ impl IsolateHandle {
20152061
callback: InterruptCallback,
20162062
data: *mut c_void,
20172063
) -> bool {
2018-
let _lock = self.0.isolate_mutex.lock().unwrap();
2019-
if self.0.isolate.is_null() {
2020-
false
2021-
} else {
2022-
unsafe { v8__Isolate__RequestInterrupt(self.0.isolate, callback, data) };
2023-
true
2024-
}
2064+
self
2065+
.with_isolate_ptr(|isolate| unsafe {
2066+
v8__Isolate__RequestInterrupt(isolate.as_ptr(), callback, data)
2067+
})
2068+
.is_some()
20252069
}
20262070
}
20272071

tests/test_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10538,7 +10538,7 @@ fn isolate_data_slots() {
1053810538
let _setup_guard = setup::parallel_test();
1053910539
let mut isolate = v8::Isolate::new(Default::default());
1054010540

10541-
assert_eq!(isolate.get_number_of_data_slots(), 2);
10541+
assert_eq!(isolate.get_number_of_data_slots(), 3);
1054210542

1054310543
let expected0 = "Bla";
1054410544
isolate.set_data(0, &expected0 as *const _ as *mut &str as *mut c_void);

0 commit comments

Comments
 (0)