Skip to content

Commit 98f2767

Browse files
committed
Store IsolateHandle data in its own allocation
1 parent 7b66456 commit 98f2767

File tree

2 files changed

+125
-81
lines changed

2 files changed

+125
-81
lines changed

src/isolate.rs

Lines changed: 124 additions & 80 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;
@@ -878,7 +879,7 @@ impl Isolate {
878879

879880
// Isolate data slots used internally by rusty_v8.
880881
const ANNEX_SLOT: u32 = 0;
881-
const INTERNAL_DATA_SLOT_COUNT: u32 = 2;
882+
const INTERNAL_DATA_SLOT_COUNT: u32 = 1;
882883

883884
#[inline(always)]
884885
fn assert_embedder_data_slot_count_and_offset_correct(&self) {
@@ -943,7 +944,7 @@ impl Isolate {
943944

944945
#[inline(always)]
945946
pub fn thread_safe_handle(&self) -> IsolateHandle {
946-
IsolateHandle::new(self)
947+
self.get_annex().isolate_handle.clone()
947948
}
948949

949950
/// See [`IsolateHandle::terminate_execution`]
@@ -968,25 +969,23 @@ impl Isolate {
968969
&mut self,
969970
create_param_allocations: Box<dyn Any>,
970971
) {
971-
let annex_arc = Arc::new(IsolateAnnex::new(self, create_param_allocations));
972-
let annex_ptr = Arc::into_raw(annex_arc);
972+
let annex_box = Box::new(IsolateAnnex::new(self, create_param_allocations));
973+
let annex_ptr = Box::into_raw(annex_box);
973974
assert!(self.get_data_internal(Self::ANNEX_SLOT).is_null());
974975
self.set_data_internal(Self::ANNEX_SLOT, annex_ptr as *mut _);
975976
}
976977

977978
unsafe fn dispose_annex(&mut self) -> Box<dyn Any> {
979+
// SAFETY: `dispose_annex()` is only called once, when the `Isolate` is dropped.
980+
let mut annex = unsafe { self.take_annex() };
981+
978982
// Set the `isolate` pointer inside the annex struct to null, so any
979983
// IsolateHandle that outlives the isolate will know that it can't call
980984
// methods on the isolate.
981-
let annex = self.get_annex_mut();
982-
{
983-
let _lock = annex.isolate_mutex.lock().unwrap();
984-
annex.isolate = null_mut();
985-
}
985+
annex.isolate_handle.dispose();
986986

987987
// Clear slots and drop owned objects that were taken out of `CreateParams`.
988-
let create_param_allocations =
989-
std::mem::replace(&mut annex.create_param_allocations, Box::new(()));
988+
let create_param_allocations = annex.create_param_allocations;
990989
annex.slots.clear();
991990

992991
// Run through any remaining guaranteed finalizers.
@@ -996,10 +995,6 @@ impl Isolate {
996995
}
997996
}
998997

999-
// Subtract one from the Arc<IsolateAnnex> reference count.
1000-
unsafe { Arc::from_raw(annex) };
1001-
self.set_data(0, null_mut());
1002-
1003998
create_param_allocations
1004999
}
10051000

@@ -1019,6 +1014,15 @@ impl Isolate {
10191014
unsafe { &mut *annex_ptr }
10201015
}
10211016

1017+
/// # Safety
1018+
/// This must only be called once.
1019+
#[inline(always)]
1020+
unsafe fn take_annex(&mut self) -> Box<IsolateAnnex> {
1021+
let annex_ptr =
1022+
self.take_data_internal(Self::ANNEX_SLOT) as *mut IsolateAnnex;
1023+
unsafe { Box::from_raw(annex_ptr) }
1024+
}
1025+
10221026
pub(crate) fn set_snapshot_creator(
10231027
&mut self,
10241028
snapshot_creator: SnapshotCreator,
@@ -1038,13 +1042,6 @@ impl Isolate {
10381042
&mut self.get_annex_mut().finalizer_map
10391043
}
10401044

1041-
fn get_annex_arc(&self) -> Arc<IsolateAnnex> {
1042-
let annex_ptr = self.get_annex();
1043-
let annex_arc = unsafe { Arc::from_raw(annex_ptr) };
1044-
let _ = Arc::into_raw(annex_arc.clone());
1045-
annex_arc
1046-
}
1047-
10481045
/// Retrieve embedder-specific data from the isolate.
10491046
/// Returns NULL if SetData has never been called for the given `slot`.
10501047
pub fn get_data(&self, slot: u32) -> *mut c_void {
@@ -1075,6 +1072,14 @@ impl Isolate {
10751072
unsafe { v8__Isolate__SetData(self.as_real_ptr(), slot, data) }
10761073
}
10771074

1075+
/// Get the value of the slot and replace it with a null pointer.
1076+
#[inline(always)]
1077+
fn take_data_internal(&mut self, slot: u32) -> *mut c_void {
1078+
let ptr = self.get_data_internal(slot);
1079+
self.set_data_internal(slot, null_mut());
1080+
ptr
1081+
}
1082+
10781083
// pub(crate) fn init_scope_root(&mut self) {
10791084
// ScopeData::new_root(self);
10801085
// }
@@ -1864,65 +1869,111 @@ pub(crate) struct IsolateAnnex {
18641869
slots: HashMap<TypeId, RawSlot, BuildTypeIdHasher>,
18651870
finalizer_map: FinalizerMap,
18661871
maybe_snapshot_creator: Option<SnapshotCreator>,
1867-
// The `isolate` and `isolate_mutex` fields are there so an `IsolateHandle`
1868-
// (which may outlive the isolate itself) can determine whether the isolate
1869-
// is still alive, and if so, get a reference to it. Safety rules:
1870-
// - The 'main thread' must lock the mutex and reset `isolate` to null just
1871-
// before the isolate is disposed.
1872-
// - Any other thread must lock the mutex while it's reading/using the
1873-
// `isolate` pointer.
1874-
isolate: *mut RealIsolate,
1875-
isolate_mutex: Mutex<()>,
1872+
isolate_handle: IsolateHandle,
18761873
}
18771874

1878-
unsafe impl Send for IsolateAnnex {}
1879-
unsafe impl Sync for IsolateAnnex {}
1880-
18811875
impl IsolateAnnex {
1882-
fn new(
1883-
isolate: &mut Isolate,
1884-
create_param_allocations: Box<dyn Any>,
1885-
) -> Self {
1876+
fn new(isolate: &Isolate, create_param_allocations: Box<dyn Any>) -> Self {
18861877
Self {
18871878
create_param_allocations,
18881879
slots: HashMap::default(),
18891880
finalizer_map: FinalizerMap::default(),
18901881
maybe_snapshot_creator: None,
1891-
isolate: isolate.as_real_ptr(),
1892-
isolate_mutex: Mutex::new(()),
1882+
isolate_handle: IsolateHandle::new(isolate),
18931883
}
18941884
}
18951885
}
18961886

18971887
impl Debug for IsolateAnnex {
18981888
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
18991889
f.debug_struct("IsolateAnnex")
1900-
.field("isolate", &self.isolate)
1901-
.field("isolate_mutex", &self.isolate_mutex)
1890+
.field("isolate_handle", &self.isolate_handle)
19021891
.finish()
19031892
}
19041893
}
19051894

1906-
/// IsolateHandle is a thread-safe reference to an Isolate. It's main use is to
1895+
pub(crate) struct IsolateHandleInner {
1896+
/// Safety invariants:
1897+
/// - The 'main thread' must lock the mutex and reset `isolate` to null just
1898+
/// before the isolate is disposed.
1899+
/// - Any other thread must lock the mutex while it's reading/using the
1900+
/// `isolate` pointer.
1901+
// These two fields can be replaced with a `Mutex<*mut RealIsolate>` once
1902+
// `Mutex::data_ptr()` is stabilized.
1903+
isolate: UnsafeCell<*mut RealIsolate>,
1904+
isolate_mutex: Mutex<()>,
1905+
}
1906+
1907+
unsafe impl Send for IsolateHandleInner {}
1908+
unsafe impl Sync for IsolateHandleInner {}
1909+
1910+
/// IsolateHandle is a thread-safe reference to an Isolate. Its main use is to
19071911
/// terminate execution of a running isolate from another thread.
19081912
///
1909-
/// It is created with Isolate::thread_safe_handle().
1913+
/// It is created with [`Isolate::thread_safe_handle()`].
19101914
///
19111915
/// IsolateHandle is Cloneable, Send, and Sync.
1912-
#[derive(Clone, Debug)]
1913-
pub struct IsolateHandle(Arc<IsolateAnnex>);
1916+
#[derive(Clone)]
1917+
pub struct IsolateHandle(Arc<IsolateHandleInner>);
19141918

1915-
impl IsolateHandle {
1916-
// This function is marked unsafe because it must be called only with either
1917-
// IsolateAnnex::mutex locked, or from the main thread associated with the V8
1918-
// isolate.
1919-
pub(crate) unsafe fn get_isolate_ptr(&self) -> *mut RealIsolate {
1920-
self.0.isolate
1919+
impl fmt::Debug for IsolateHandle {
1920+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1921+
if let Ok(_lock) = self.0.isolate_mutex.try_lock() {
1922+
// SAFETY: mutex lock is held
1923+
let ptr = unsafe { *self.0.isolate.get() };
1924+
f.debug_struct("IsolateHandle")
1925+
.field("isolate_ptr", &ptr)
1926+
.finish()
1927+
} else {
1928+
f.debug_struct("IsolateHandle").finish_non_exhaustive()
1929+
}
19211930
}
1931+
}
19221932

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

19281979
/// Forcefully terminate the current thread of JavaScript execution
@@ -1934,13 +1985,11 @@ impl IsolateHandle {
19341985
/// Returns false if Isolate was already destroyed.
19351986
#[inline(always)]
19361987
pub fn terminate_execution(&self) -> bool {
1937-
let _lock = self.0.isolate_mutex.lock().unwrap();
1938-
if self.0.isolate.is_null() {
1939-
false
1940-
} else {
1941-
unsafe { v8__Isolate__TerminateExecution(self.0.isolate) };
1942-
true
1943-
}
1988+
self
1989+
.with_isolate_ptr(|isolate| unsafe {
1990+
v8__Isolate__TerminateExecution(isolate.as_ptr())
1991+
})
1992+
.is_some()
19441993
}
19451994

19461995
/// Resume execution capability in the given isolate, whose execution
@@ -1959,13 +2008,11 @@ impl IsolateHandle {
19592008
/// Returns false if Isolate was already destroyed.
19602009
#[inline(always)]
19612010
pub fn cancel_terminate_execution(&self) -> bool {
1962-
let _lock = self.0.isolate_mutex.lock().unwrap();
1963-
if self.0.isolate.is_null() {
1964-
false
1965-
} else {
1966-
unsafe { v8__Isolate__CancelTerminateExecution(self.0.isolate) };
1967-
true
1968-
}
2011+
self
2012+
.with_isolate_ptr(|isolate| unsafe {
2013+
v8__Isolate__CancelTerminateExecution(isolate.as_ptr())
2014+
})
2015+
.is_some()
19692016
}
19702017

19712018
/// Is V8 terminating JavaScript execution.
@@ -1978,12 +2025,11 @@ impl IsolateHandle {
19782025
/// Returns false if Isolate was already destroyed.
19792026
#[inline(always)]
19802027
pub fn is_execution_terminating(&self) -> bool {
1981-
let _lock = self.0.isolate_mutex.lock().unwrap();
1982-
if self.0.isolate.is_null() {
1983-
false
1984-
} else {
1985-
unsafe { v8__Isolate__IsExecutionTerminating(self.0.isolate) }
1986-
}
2028+
self
2029+
.with_isolate_ptr(|isolate| unsafe {
2030+
v8__Isolate__IsExecutionTerminating(isolate.as_ptr())
2031+
})
2032+
.unwrap_or(false)
19872033
}
19882034

19892035
/// Request V8 to interrupt long running JavaScript code and invoke
@@ -2003,13 +2049,11 @@ impl IsolateHandle {
20032049
callback: InterruptCallback,
20042050
data: *mut c_void,
20052051
) -> bool {
2006-
let _lock = self.0.isolate_mutex.lock().unwrap();
2007-
if self.0.isolate.is_null() {
2008-
false
2009-
} else {
2010-
unsafe { v8__Isolate__RequestInterrupt(self.0.isolate, callback, data) };
2011-
true
2012-
}
2052+
self
2053+
.with_isolate_ptr(|isolate| unsafe {
2054+
v8__Isolate__RequestInterrupt(isolate.as_ptr(), callback, data)
2055+
})
2056+
.is_some()
20132057
}
20142058
}
20152059

tests/test_api.rs

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

10537-
assert_eq!(isolate.get_number_of_data_slots(), 2);
10537+
assert_eq!(isolate.get_number_of_data_slots(), 1);
1053810538

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

0 commit comments

Comments
 (0)