Skip to content

Commit 8e11074

Browse files
committed
feat: isolate groups
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 ```
1 parent 45edd18 commit 8e11074

File tree

5 files changed

+174
-8
lines changed

5 files changed

+174
-8
lines changed

src/binding.cc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,30 @@ bool v8__V8__Dispose() { return v8::V8::Dispose(); }
152152

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

155-
v8::Isolate* v8__Isolate__New(const v8::Isolate::CreateParams& params) {
156-
return v8::Isolate::New(params);
155+
v8::internal::IsolateGroup* v8__IsolateGroup__GetDefault() {
156+
return make_pod<v8::internal::IsolateGroup*>(v8::IsolateGroup::GetDefault());
157+
}
158+
159+
bool v8__IsolateGroup__CanCreateNewGroups() {
160+
return v8::IsolateGroup::CanCreateNewGroups();
161+
}
162+
163+
v8::internal::IsolateGroup* v8__IsolateGroup__Create() {
164+
return make_pod<v8::internal::IsolateGroup*>(v8::IsolateGroup::Create());
165+
}
166+
167+
void v8__IsolateGroup__DESTRUCT(v8::IsolateGroup* self) {
168+
self->~IsolateGroup();
169+
}
170+
171+
bool v8__IsolateGroup__EQ(const v8::IsolateGroup& self,
172+
const v8::IsolateGroup& other) {
173+
return self == other;
174+
}
175+
176+
v8::Isolate* v8__Isolate__New(const v8::IsolateGroup& group,
177+
const v8::Isolate::CreateParams& params) {
178+
return v8::Isolate::New(group, params);
157179
}
158180

159181
void v8__Isolate__Dispose(v8::Isolate* isolate) { isolate->Dispose(); }

src/isolate.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::FixedArray;
3131
use crate::Function;
3232
use crate::FunctionCodeHandling;
3333
use crate::HandleScope;
34+
use crate::IsolateGroup;
3435
use crate::Local;
3536
use crate::Message;
3637
use crate::Module;
@@ -417,7 +418,10 @@ pub type UseCounterCallback = extern "C" fn(&mut Isolate, UseCounterFeature);
417418
extern "C" {
418419
static v8__internal__Internals__kIsolateEmbedderDataOffset: int;
419420

420-
fn v8__Isolate__New(params: *const raw::CreateParams) -> *mut Isolate;
421+
fn v8__Isolate__New(
422+
group: *const IsolateGroup,
423+
params: *const raw::CreateParams,
424+
) -> *mut Isolate;
421425
fn v8__Isolate__Dispose(this: *mut Isolate);
422426
fn v8__Isolate__GetNumberOfDataSlots(this: *const Isolate) -> u32;
423427
fn v8__Isolate__Enter(this: *mut Isolate);
@@ -628,10 +632,10 @@ impl Isolate {
628632
);
629633
}
630634

631-
fn new_impl(params: CreateParams) -> *mut Isolate {
635+
fn new_impl(group: &IsolateGroup, params: CreateParams) -> *mut Isolate {
632636
crate::V8::assert_initialized();
633637
let (raw_create_params, create_param_allocations) = params.finalize();
634-
let cxx_isolate = unsafe { v8__Isolate__New(&raw_create_params) };
638+
let cxx_isolate = unsafe { v8__Isolate__New(group, &raw_create_params) };
635639
let isolate = unsafe { &mut *cxx_isolate };
636640
isolate.initialize(create_param_allocations);
637641
cxx_isolate
@@ -642,16 +646,31 @@ impl Isolate {
642646
self.create_annex(create_param_allocations);
643647
}
644648

645-
/// Creates a new isolate. Does not change the currently entered
649+
/// Creates a new isolate. Does not change the currently entered
646650
/// isolate.
647651
///
648652
/// When an isolate is no longer used its resources should be freed
649-
/// by calling V8::dispose(). Using the delete operator is not allowed.
653+
/// by calling V8::dispose(). Using the delete operator is not allowed.
650654
///
651655
/// V8::initialize() must have run prior to this.
652656
#[allow(clippy::new_ret_no_self)]
653657
pub fn new(params: CreateParams) -> OwnedIsolate {
654-
OwnedIsolate::new(Self::new_impl(params))
658+
let group = IsolateGroup::get_default();
659+
OwnedIsolate::new(Self::new_impl(&group, params))
660+
}
661+
662+
/// Creates a new isolate. Does not change the currently entered
663+
/// isolate.
664+
///
665+
/// When an isolate is no longer used its resources should be freed
666+
/// by calling V8::dispose(). Using the delete operator is not allowed.
667+
///
668+
/// V8::initialize() must have run prior to this.
669+
pub fn new_with_group(
670+
group: &IsolateGroup,
671+
params: CreateParams,
672+
) -> OwnedIsolate {
673+
OwnedIsolate::new(Self::new_impl(group, params))
655674
}
656675

657676
#[allow(clippy::new_ret_no_self)]

src/isolate_group.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
2+
3+
use crate::support::Opaque;
4+
5+
#[repr(C)]
6+
struct InternalIsolateGroup(Opaque);
7+
8+
extern "C" {
9+
fn v8__IsolateGroup__GetDefault() -> *const InternalIsolateGroup;
10+
fn v8__IsolateGroup__CanCreateNewGroups() -> bool;
11+
fn v8__IsolateGroup__Create() -> *const InternalIsolateGroup;
12+
13+
fn v8__IsolateGroup__DESTRUCT(this: *mut IsolateGroup);
14+
fn v8__IsolateGroup__EQ(
15+
this: *const IsolateGroup,
16+
other: *const IsolateGroup,
17+
) -> bool;
18+
}
19+
20+
/// The set of V8 isolates in a process is partitioned into groups. Each group
21+
/// has its own sandbox (if V8 was configured with support for the sandbox) and
22+
/// pointer-compression cage (if configured with pointer compression).
23+
///
24+
/// By default, all isolates are placed in the same group. This is the most
25+
/// efficient configuration in terms of speed and memory use. However, with
26+
/// pointer compression enabled, total heap usage of isolates in a group cannot
27+
/// exceed 4 GB, not counting array buffers and other off-heap storage. Using
28+
/// multiple isolate groups can allow embedders to allocate more than 4GB of
29+
/// objects with pointer compression enabled, if the embedder's use case can
30+
/// span multiple isolates.
31+
///
32+
/// Creating an isolate group reserves a range of virtual memory addresses. A
33+
/// group's memory mapping will be released when the last isolate in the group
34+
/// is disposed, and there are no more live IsolateGroup objects that refer to
35+
/// it.
36+
///
37+
/// Note that Isolate groups are reference counted, and the IsolateGroup type is
38+
/// a reference to one.
39+
///
40+
/// Note that it's not going to be possible to pass shared JS objects across
41+
/// IsolateGroup boundary.
42+
#[repr(C)]
43+
pub struct IsolateGroup(*const InternalIsolateGroup);
44+
45+
unsafe impl Send for IsolateGroup {}
46+
unsafe impl Sync for IsolateGroup {}
47+
48+
impl IsolateGroup {
49+
/// Return true if new isolate groups can be created at run-time, or false if
50+
/// all isolates must be in the same group.
51+
pub fn can_create_new_groups() -> bool {
52+
unsafe { v8__IsolateGroup__CanCreateNewGroups() }
53+
}
54+
55+
/// Get the default isolate group. If this V8's build configuration only
56+
/// supports a single group, this is a reference to that single group.
57+
/// Otherwise this is a group like any other, distinguished only in that it is
58+
/// the first group.
59+
pub fn get_default() -> Self {
60+
IsolateGroup(unsafe { v8__IsolateGroup__GetDefault() })
61+
}
62+
63+
/// Create a new isolate group. If this V8's build configuration only supports
64+
/// a single group, abort.
65+
pub fn create() -> Self {
66+
IsolateGroup(unsafe { v8__IsolateGroup__Create() })
67+
}
68+
}
69+
70+
impl Default for IsolateGroup {
71+
fn default() -> Self {
72+
IsolateGroup::get_default()
73+
}
74+
}
75+
76+
impl Drop for IsolateGroup {
77+
fn drop(&mut self) {
78+
unsafe { v8__IsolateGroup__DESTRUCT(self) }
79+
}
80+
}
81+
82+
impl Eq for IsolateGroup {}
83+
84+
impl PartialEq for IsolateGroup {
85+
fn eq(&self, other: &Self) -> bool {
86+
unsafe { v8__IsolateGroup__EQ(self, other) }
87+
}
88+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod handle;
4848
pub mod icu;
4949
mod isolate;
5050
mod isolate_create_params;
51+
mod isolate_group;
5152
mod microtask;
5253
mod module;
5354
mod name;
@@ -126,6 +127,7 @@ pub use isolate::UseCounterCallback;
126127
pub use isolate::UseCounterFeature;
127128
pub use isolate::WasmAsyncSuccess;
128129
pub use isolate_create_params::CreateParams;
130+
pub use isolate_group::IsolateGroup;
129131
pub use microtask::MicrotaskQueue;
130132
pub use module::*;
131133
pub use object::*;

tests/test_api.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,41 @@ fn microtasks() {
630630
}
631631
}
632632

633+
#[test]
634+
fn isolate_groups() {
635+
let _setup_guard = setup::parallel_test();
636+
637+
if !v8::IsolateGroup::can_create_new_groups() {
638+
println!("Skipping 'isolate_groups' test: current build does not support isolate groups");
639+
return;
640+
}
641+
642+
fn test_isolate_with_group(
643+
group: v8::IsolateGroup,
644+
) -> std::thread::JoinHandle<()> {
645+
std::thread::spawn(move || {
646+
let isolate =
647+
&mut v8::Isolate::new_with_group(&group, Default::default());
648+
let scope = &mut v8::HandleScope::new(isolate);
649+
let context = v8::Context::new(scope, Default::default());
650+
let scope = &mut v8::ContextScope::new(scope, context);
651+
let result = eval(scope, "1 + 1").unwrap().int32_value(scope).unwrap();
652+
assert_eq!(result, 2);
653+
})
654+
}
655+
656+
let group1 = v8::IsolateGroup::create();
657+
let group2 = v8::IsolateGroup::create();
658+
659+
let t0 = test_isolate_with_group(Default::default());
660+
let t1 = test_isolate_with_group(group1);
661+
let t2 = test_isolate_with_group(group2);
662+
663+
t0.join().unwrap();
664+
t1.join().unwrap();
665+
t2.join().unwrap();
666+
}
667+
633668
#[test]
634669
#[should_panic(
635670
expected = "v8::OwnedIsolate instances must be dropped in the reverse order of creation. They are entered upon creation and exited upon being dropped."

0 commit comments

Comments
 (0)