Skip to content

Commit

Permalink
feat: isolate groups
Browse files Browse the repository at this point in the history
Currently requires the following gn flags to work:
```
v8_enable_external_code_space = true
v8_enable_pointer_compression = true
v8_enable_pointer_compression_shared_cage = false
v8_enable_sandbox = false
```
  • Loading branch information
piscisaureus committed Feb 7, 2025
1 parent 45edd18 commit 348b8a5
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 8 deletions.
26 changes: 24 additions & 2 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,30 @@ bool v8__V8__Dispose() { return v8::V8::Dispose(); }

void v8__V8__DisposePlatform() { v8::V8::DisposePlatform(); }

v8::Isolate* v8__Isolate__New(const v8::Isolate::CreateParams& params) {
return v8::Isolate::New(params);
v8::internal::IsolateGroup* v8__IsolateGroup__GetDefault() {
return make_pod<v8::internal::IsolateGroup*>(v8::IsolateGroup::GetDefault());
}

bool v8__IsolateGroup__CanCreateNewGroups() {
return v8::IsolateGroup::CanCreateNewGroups();
}

v8::internal::IsolateGroup* v8__IsolateGroup__Create() {
return make_pod<v8::internal::IsolateGroup*>(v8::IsolateGroup::Create());
}

void v8__IsolateGroup__DESTRUCT(v8::IsolateGroup* self) {
self->~IsolateGroup();
}

bool v8__IsolateGroup__EQ(const v8::IsolateGroup& self,
const v8::IsolateGroup& other) {
return self == other;
}

v8::Isolate* v8__Isolate__New(const v8::IsolateGroup& group,
const v8::Isolate::CreateParams& params) {
return v8::Isolate::New(group, params);
}

void v8__Isolate__Dispose(v8::Isolate* isolate) { isolate->Dispose(); }
Expand Down
31 changes: 25 additions & 6 deletions src/isolate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::FixedArray;
use crate::Function;
use crate::FunctionCodeHandling;
use crate::HandleScope;
use crate::IsolateGroup;
use crate::Local;
use crate::Message;
use crate::Module;
Expand Down Expand Up @@ -417,7 +418,10 @@ pub type UseCounterCallback = extern "C" fn(&mut Isolate, UseCounterFeature);
extern "C" {
static v8__internal__Internals__kIsolateEmbedderDataOffset: int;

fn v8__Isolate__New(params: *const raw::CreateParams) -> *mut Isolate;
fn v8__Isolate__New(
group: *const IsolateGroup,
params: *const raw::CreateParams,
) -> *mut Isolate;
fn v8__Isolate__Dispose(this: *mut Isolate);
fn v8__Isolate__GetNumberOfDataSlots(this: *const Isolate) -> u32;
fn v8__Isolate__Enter(this: *mut Isolate);
Expand Down Expand Up @@ -628,10 +632,10 @@ impl Isolate {
);
}

fn new_impl(params: CreateParams) -> *mut Isolate {
fn new_impl(group: &IsolateGroup, params: CreateParams) -> *mut Isolate {
crate::V8::assert_initialized();
let (raw_create_params, create_param_allocations) = params.finalize();
let cxx_isolate = unsafe { v8__Isolate__New(&raw_create_params) };
let cxx_isolate = unsafe { v8__Isolate__New(group, &raw_create_params) };
let isolate = unsafe { &mut *cxx_isolate };
isolate.initialize(create_param_allocations);
cxx_isolate
Expand All @@ -642,16 +646,31 @@ impl Isolate {
self.create_annex(create_param_allocations);
}

/// Creates a new isolate. Does not change the currently entered
/// Creates a new isolate. Does not change the currently entered
/// isolate.
///
/// When an isolate is no longer used its resources should be freed
/// by calling V8::dispose(). Using the delete operator is not allowed.
/// by calling V8::dispose(). Using the delete operator is not allowed.
///
/// V8::initialize() must have run prior to this.
#[allow(clippy::new_ret_no_self)]
pub fn new(params: CreateParams) -> OwnedIsolate {
OwnedIsolate::new(Self::new_impl(params))
let group = IsolateGroup::get_default();
OwnedIsolate::new(Self::new_impl(&group, params))
}

/// Creates a new isolate. Does not change the currently entered
/// isolate.
///
/// When an isolate is no longer used its resources should be freed
/// by calling V8::dispose(). Using the delete operator is not allowed.
///
/// V8::initialize() must have run prior to this.
pub fn new_with_group(
group: &IsolateGroup,
params: CreateParams,
) -> OwnedIsolate {
OwnedIsolate::new(Self::new_impl(group, params))
}

#[allow(clippy::new_ret_no_self)]
Expand Down
88 changes: 88 additions & 0 deletions src/isolate_group.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.

use crate::support::Opaque;

#[repr(C)]
struct InternalIsolateGroup(Opaque);

extern "C" {
fn v8__IsolateGroup__GetDefault() -> *const InternalIsolateGroup;
fn v8__IsolateGroup__CanCreateNewGroups() -> bool;
fn v8__IsolateGroup__Create() -> *const InternalIsolateGroup;

fn v8__IsolateGroup__DESTRUCT(this: *mut IsolateGroup);
fn v8__IsolateGroup__EQ(
this: *const IsolateGroup,
other: *const IsolateGroup,
) -> bool;
}

/// The set of V8 isolates in a process is partitioned into groups. Each group
/// has its own sandbox (if V8 was configured with support for the sandbox) and
/// pointer-compression cage (if configured with pointer compression).
///
/// By default, all isolates are placed in the same group. This is the most
/// efficient configuration in terms of speed and memory use. However, with
/// pointer compression enabled, total heap usage of isolates in a group cannot
/// exceed 4 GB, not counting array buffers and other off-heap storage. Using
/// multiple isolate groups can allow embedders to allocate more than 4GB of
/// objects with pointer compression enabled, if the embedder's use case can
/// span multiple isolates.
///
/// Creating an isolate group reserves a range of virtual memory addresses. A
/// group's memory mapping will be released when the last isolate in the group
/// is disposed, and there are no more live IsolateGroup objects that refer to
/// it.
///
/// Note that Isolate groups are reference counted, and the IsolateGroup type is
/// a reference to one.
///
/// Note that it's not going to be possible to pass shared JS objects across
/// IsolateGroup boundary.
#[repr(C)]
pub struct IsolateGroup(*const InternalIsolateGroup);

unsafe impl Send for IsolateGroup {}
unsafe impl Sync for IsolateGroup {}

impl IsolateGroup {
/// Return true if new isolate groups can be created at run-time, or false if
/// all isolates must be in the same group.
pub fn can_create_new_groups() -> bool {
unsafe { v8__IsolateGroup__CanCreateNewGroups() }
}

/// Get the default isolate group. If this V8's build configuration only
/// supports a single group, this is a reference to that single group.
/// Otherwise this is a group like any other, distinguished only in that it is
/// the first group.
pub fn get_default() -> Self {
IsolateGroup(unsafe { v8__IsolateGroup__GetDefault() })
}

/// Create a new isolate group. If this V8's build configuration only supports
/// a single group, abort.
pub fn create() -> Self {
IsolateGroup(unsafe { v8__IsolateGroup__Create() })
}
}

impl Default for IsolateGroup {
fn default() -> Self {
IsolateGroup::get_default()
}
}

impl Drop for IsolateGroup {
fn drop(&mut self) {
unsafe { v8__IsolateGroup__DESTRUCT(self) }
}
}

impl Eq for IsolateGroup {}

impl PartialEq for IsolateGroup {
fn eq(&self, other: &Self) -> bool {
unsafe { v8__IsolateGroup__EQ(self, other) }
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod handle;
pub mod icu;
mod isolate;
mod isolate_create_params;
mod isolate_group;
mod microtask;
mod module;
mod name;
Expand Down Expand Up @@ -126,6 +127,7 @@ pub use isolate::UseCounterCallback;
pub use isolate::UseCounterFeature;
pub use isolate::WasmAsyncSuccess;
pub use isolate_create_params::CreateParams;
pub use isolate_group::IsolateGroup;
pub use microtask::MicrotaskQueue;
pub use module::*;
pub use object::*;
Expand Down
34 changes: 34 additions & 0 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,40 @@ fn microtasks() {
}
}

#[test]
fn isolate_groups() {
let _setup_guard = setup::parallel_test();

if !v8::IsolateGroup::can_create_new_groups() {
println!("Skipping 'isolate_groups' test: current build does not support isolate groups");
}

fn test_isolate_with_group(
group: v8::IsolateGroup,
) -> std::thread::JoinHandle<()> {
std::thread::spawn(move || {
let isolate =
&mut v8::Isolate::new_with_group(&group, Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope, Default::default());
let scope = &mut v8::ContextScope::new(scope, context);
let result = eval(scope, "1 + 1").unwrap().int32_value(scope).unwrap();
assert_eq!(result, 2);
})
}

let group1 = v8::IsolateGroup::create();
let group2 = v8::IsolateGroup::create();

let t0 = test_isolate_with_group(Default::default());
let t1 = test_isolate_with_group(group1);
let t2 = test_isolate_with_group(group2);

t0.join().unwrap();
t1.join().unwrap();
t2.join().unwrap();
}

#[test]
#[should_panic(
expected = "v8::OwnedIsolate instances must be dropped in the reverse order of creation. They are entered upon creation and exited upon being dropped."
Expand Down

0 comments on commit 348b8a5

Please sign in to comment.