use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ptr::NonNull;
use std::sync::Arc;
use crate::Isolate;
use crate::IsolateHandle;
use crate::isolate::RealIsolate;
unsafe extern "C" {
fn v8__Locker__CONSTRUCT(buf: *mut RawLocker, isolate: *mut RealIsolate);
fn v8__Locker__DESTRUCT(this: *mut RawLocker);
pub(crate) fn v8__Locker__IsLocked(isolate: *const RealIsolate) -> bool;
fn v8__Unlocker__CONSTRUCT(buf: *mut RawUnlocker, isolate: *mut RealIsolate);
fn v8__Unlocker__DESTRUCT(this: *mut RawUnlocker);
fn v8__Isolate__Enter(isolate: *mut RealIsolate);
fn v8__Isolate__Exit(isolate: *mut RealIsolate);
fn v8__Isolate__TryGetCurrent() -> *mut RealIsolate;
}
pub(crate) fn thread_holds_lock(isolate: *mut RealIsolate) -> bool {
unsafe { v8__Locker__IsLocked(isolate) }
}
#[repr(C)]
pub(crate) struct RawLocker([usize; 2]);
#[repr(C)]
pub(crate) struct RawUnlocker([usize; 1]);
#[derive(Debug)]
pub struct SharedIsolate {
inner: Arc<SharedIsolateInner>,
}
#[derive(Debug)]
struct SharedIsolateInner {
cxx_isolate: NonNull<RealIsolate>,
isolate_handle: IsolateHandle,
}
unsafe impl Send for SharedIsolateInner {}
unsafe impl Sync for SharedIsolateInner {}
impl SharedIsolate {
pub(crate) fn new(
cxx_isolate: NonNull<RealIsolate>,
isolate_handle: IsolateHandle,
) -> Self {
Self {
inner: Arc::new(SharedIsolateInner {
cxx_isolate,
isolate_handle,
}),
}
}
pub(crate) fn as_real_ptr(&self) -> *mut RealIsolate {
self.inner.cxx_isolate.as_ptr()
}
pub fn lock(&self) -> Locker<'_> {
Locker::new(self)
}
pub fn thread_safe_handle(&self) -> IsolateHandle {
self.inner.isolate_handle.clone()
}
}
impl Drop for SharedIsolateInner {
fn drop(&mut self) {
unsafe {
let mut isolate = Isolate::from_non_null(self.cxx_isolate);
let ptr = self.cxx_isolate.as_ptr();
let mut raw = Box::new(RawLocker([0; 2]));
v8__Locker__CONSTRUCT(&mut *raw, ptr);
isolate
.global_liveness()
.as_ref()
.close_deferred_global_resets();
v8__Locker__DESTRUCT(&mut *raw);
let (annex_ptr, _create_param_allocations) =
isolate.prepare_annex_for_dispose();
Isolate::run_remaining_guaranteed_finalizers(annex_ptr);
crate::Platform::notify_isolate_shutdown(
&crate::V8::get_current_platform(),
&isolate,
);
isolate.dispose();
Isolate::finish_annex_dispose(annex_ptr);
}
}
}
pub struct Locker<'s> {
raw: Box<RawLocker>,
cxx_isolate: NonNull<RealIsolate>,
inner: ManuallyDrop<Arc<SharedIsolateInner>>,
_shared: PhantomData<&'s SharedIsolate>,
}
impl<'s> Locker<'s> {
fn new(shared: &'s SharedIsolate) -> Self {
let ptr = shared.as_real_ptr();
unsafe {
assert!(
!thread_holds_lock(ptr),
"attempted to lock an isolate that is already locked by this thread"
);
assert!(
v8__Isolate__TryGetCurrent().is_null(),
"attempted to lock a shared isolate while another isolate is entered"
);
let mut raw = Box::new(RawLocker([0; 2]));
v8__Locker__CONSTRUCT(&mut *raw, ptr);
v8__Isolate__Enter(ptr);
let locker = Self {
raw,
cxx_isolate: shared.inner.cxx_isolate,
inner: ManuallyDrop::new(Arc::clone(&shared.inner)),
_shared: PhantomData,
};
locker
.global_liveness()
.as_ref()
.maybe_drain_deferred_global_resets();
locker
}
}
pub fn unlock<R>(&mut self, f: impl FnOnce() -> R) -> R {
let ptr = self.cxx_isolate.as_ptr();
unsafe {
assert!(
std::ptr::eq(ptr, v8__Isolate__TryGetCurrent()),
"Locker::unlock called while another isolate was entered on top of \
this one"
);
self
.global_liveness()
.as_ref()
.maybe_drain_deferred_global_resets();
}
unsafe { v8__Isolate__Exit(ptr) };
let mut raw = Box::new(RawUnlocker([0; 1]));
unsafe { v8__Unlocker__CONSTRUCT(&mut *raw, ptr) };
let _relock = RelockGuard {
raw,
cxx_isolate: self.cxx_isolate,
};
let result = f();
assert!(
unsafe { v8__Isolate__TryGetCurrent().is_null() },
"Locker::unlock closure returned while an isolate was still entered"
);
result
}
}
struct RelockGuard {
raw: Box<RawUnlocker>,
cxx_isolate: NonNull<RealIsolate>,
}
impl Drop for RelockGuard {
fn drop(&mut self) {
unsafe {
v8__Unlocker__DESTRUCT(&mut *self.raw);
v8__Isolate__Enter(self.cxx_isolate.as_ptr());
Isolate::from_non_null(self.cxx_isolate)
.global_liveness()
.as_ref()
.maybe_drain_deferred_global_resets();
}
}
}
impl Drop for Locker<'_> {
fn drop(&mut self) {
unsafe {
self
.global_liveness()
.as_ref()
.maybe_drain_deferred_global_resets();
assert!(
std::ptr::eq(self.cxx_isolate.as_ptr(), v8__Isolate__TryGetCurrent()),
"Locker dropped while its isolate was not the entered one; lockers \
must be dropped in reverse order of creation"
);
v8__Isolate__Exit(self.cxx_isolate.as_ptr());
v8__Locker__DESTRUCT(&mut *self.raw);
ManuallyDrop::drop(&mut self.inner);
}
}
}
impl Deref for Locker<'_> {
type Target = Isolate;
fn deref(&self) -> &Isolate {
unsafe { Isolate::from_raw_ref(&self.cxx_isolate) }
}
}
impl DerefMut for Locker<'_> {
fn deref_mut(&mut self) -> &mut Isolate {
unsafe { Isolate::from_raw_ref_mut(&mut self.cxx_isolate) }
}
}