mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-12-25 08:39:15 -05:00
Add support for setting V8 heap limits and using a near-heap-limit callback (#427)
This commit is contained in:
parent
31036d8130
commit
b0dc42f900
5 changed files with 158 additions and 5 deletions
|
@ -178,6 +178,18 @@ bool v8__Isolate__AddMessageListener(v8::Isolate* isolate,
|
|||
return isolate->AddMessageListener(callback);
|
||||
}
|
||||
|
||||
void v8__Isolate__AddNearHeapLimitCallback(v8::Isolate* isolate,
|
||||
v8::NearHeapLimitCallback callback,
|
||||
void* data) {
|
||||
isolate->AddNearHeapLimitCallback(callback, data);
|
||||
}
|
||||
|
||||
void v8__Isolate__RemoveNearHeapLimitCallback(
|
||||
v8::Isolate* isolate, v8::NearHeapLimitCallback callback,
|
||||
size_t heap_limit) {
|
||||
isolate->RemoveNearHeapLimitCallback(callback, heap_limit);
|
||||
}
|
||||
|
||||
const v8::Value* v8__Isolate__ThrowException(v8::Isolate* isolate,
|
||||
const v8::Value& exception) {
|
||||
return local_to_ptr(isolate->ThrowException(ptr_to_local(&exception)));
|
||||
|
@ -204,6 +216,13 @@ size_t v8__Isolate__CreateParams__SIZEOF() {
|
|||
return sizeof(v8::Isolate::CreateParams);
|
||||
}
|
||||
|
||||
void v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
|
||||
v8::ResourceConstraints* constraints, size_t initial_heap_size_in_bytes,
|
||||
size_t maximum_heap_size_in_bytes) {
|
||||
constraints->ConfigureDefaultsFromHeapSize(initial_heap_size_in_bytes,
|
||||
maximum_heap_size_in_bytes);
|
||||
}
|
||||
|
||||
void v8__HandleScope__CONSTRUCT(uninit_t<v8::HandleScope>* buf,
|
||||
v8::Isolate* isolate) {
|
||||
construct_in_place<v8::HandleScope>(buf, isolate);
|
||||
|
@ -743,11 +762,10 @@ MaybeBool v8__Object__SetAccessor(const v8::Object& self,
|
|||
ptr_to_local(&context), ptr_to_local(&key), getter));
|
||||
}
|
||||
|
||||
MaybeBool v8__Object__SetAccessorWithSetter(const v8::Object& self,
|
||||
const v8::Context& context,
|
||||
const v8::Name& key,
|
||||
v8::AccessorNameGetterCallback getter,
|
||||
v8::AccessorNameSetterCallback setter) {
|
||||
MaybeBool v8__Object__SetAccessorWithSetter(
|
||||
const v8::Object& self, const v8::Context& context, const v8::Name& key,
|
||||
v8::AccessorNameGetterCallback getter,
|
||||
v8::AccessorNameSetterCallback setter) {
|
||||
return maybe_to_maybe_bool(ptr_to_local(&self)->SetAccessor(
|
||||
ptr_to_local(&context), ptr_to_local(&key), getter, setter));
|
||||
}
|
||||
|
|
|
@ -69,6 +69,12 @@ pub type HostImportModuleDynamicallyCallback = extern "C" fn(
|
|||
pub type InterruptCallback =
|
||||
extern "C" fn(isolate: &mut Isolate, data: *mut c_void);
|
||||
|
||||
pub type NearHeapLimitCallback = extern "C" fn(
|
||||
data: *mut c_void,
|
||||
current_heap_limit: usize,
|
||||
initial_heap_limit: usize,
|
||||
) -> usize;
|
||||
|
||||
extern "C" {
|
||||
fn v8__Isolate__New(params: *const raw::CreateParams) -> *mut Isolate;
|
||||
fn v8__Isolate__Dispose(this: *mut Isolate);
|
||||
|
@ -86,6 +92,16 @@ extern "C" {
|
|||
isolate: *mut Isolate,
|
||||
callback: MessageCallback,
|
||||
) -> bool;
|
||||
fn v8__Isolate__AddNearHeapLimitCallback(
|
||||
isolate: *mut Isolate,
|
||||
callback: NearHeapLimitCallback,
|
||||
data: *mut c_void,
|
||||
);
|
||||
fn v8__Isolate__RemoveNearHeapLimitCallback(
|
||||
isolate: *mut Isolate,
|
||||
callback: NearHeapLimitCallback,
|
||||
heap_limit: usize,
|
||||
);
|
||||
fn v8__Isolate__SetPromiseRejectCallback(
|
||||
isolate: *mut Isolate,
|
||||
callback: PromiseRejectCallback,
|
||||
|
@ -345,6 +361,32 @@ impl Isolate {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add a callback to invoke in case the heap size is close to the heap limit.
|
||||
/// If multiple callbacks are added, only the most recently added callback is
|
||||
/// invoked.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)] // False positive.
|
||||
pub fn add_near_heap_limit_callback(
|
||||
&mut self,
|
||||
callback: NearHeapLimitCallback,
|
||||
data: *mut c_void,
|
||||
) {
|
||||
unsafe { v8__Isolate__AddNearHeapLimitCallback(self, callback, data) };
|
||||
}
|
||||
|
||||
/// Remove the given callback and restore the heap limit to the given limit.
|
||||
/// If the given limit is zero, then it is ignored. If the current heap size
|
||||
/// is greater than the given limit, then the heap limit is restored to the
|
||||
/// minimal limit that is possible for the current heap size.
|
||||
pub fn remove_near_heap_limit_callback(
|
||||
&mut self,
|
||||
callback: NearHeapLimitCallback,
|
||||
heap_limit: usize,
|
||||
) {
|
||||
unsafe {
|
||||
v8__Isolate__RemoveNearHeapLimitCallback(self, callback, heap_limit)
|
||||
};
|
||||
}
|
||||
|
||||
/// Runs the default MicrotaskQueue until it gets empty.
|
||||
/// Any exceptions thrown by microtask callbacks are swallowed.
|
||||
pub fn run_microtasks(&mut self) {
|
||||
|
|
|
@ -114,6 +114,35 @@ impl CreateParams {
|
|||
self
|
||||
}
|
||||
|
||||
/// Configures the constraints with reasonable default values based on the
|
||||
/// provided lower and upper bounds.
|
||||
///
|
||||
/// By default V8 starts with a small heap and dynamically grows it to match
|
||||
/// the set of live objects. This may lead to ineffective garbage collections
|
||||
/// at startup if the live set is large. Setting the initial heap size avoids
|
||||
/// such garbage collections. Note that this does not affect young generation
|
||||
/// garbage collections.
|
||||
///
|
||||
/// When the heap size approaches `max`, V8 will perform series of
|
||||
/// garbage collections and invoke the
|
||||
/// [NearHeapLimitCallback](struct.Isolate.html#method.add_near_heap_limit_callback).
|
||||
/// If the garbage collections do not help and the callback does not
|
||||
/// increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory.
|
||||
///
|
||||
/// The heap size includes both the young and the old generation.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `initial` - The initial heap size or zero in bytes
|
||||
/// * `max` - The hard limit for the heap size in bytes
|
||||
pub fn heap_limits(mut self, initial: usize, max: usize) -> Self {
|
||||
self
|
||||
.raw
|
||||
.constraints
|
||||
.configure_defaults_from_heap_size(initial, max);
|
||||
self
|
||||
}
|
||||
|
||||
fn set_fallback_defaults(mut self) -> Self {
|
||||
if self.raw.array_buffer_allocator_shared.is_null() {
|
||||
self = self.array_buffer_allocator(array_buffer::new_default_allocator());
|
||||
|
@ -201,4 +230,28 @@ pub(crate) mod raw {
|
|||
initial_young_generation_size_: usize,
|
||||
stack_limit_: *mut u32,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
|
||||
constraints: *mut ResourceConstraints,
|
||||
initial_heap_size_in_bytes: usize,
|
||||
maximum_heap_size_in_bytes: usize,
|
||||
);
|
||||
}
|
||||
|
||||
impl ResourceConstraints {
|
||||
pub fn configure_defaults_from_heap_size(
|
||||
&mut self,
|
||||
initial_heap_size_in_bytes: usize,
|
||||
maximum_heap_size_in_bytes: usize,
|
||||
) {
|
||||
unsafe {
|
||||
v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
|
||||
self,
|
||||
initial_heap_size_in_bytes,
|
||||
maximum_heap_size_in_bytes,
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ pub use isolate::HostInitializeImportMetaObjectCallback;
|
|||
pub use isolate::Isolate;
|
||||
pub use isolate::IsolateHandle;
|
||||
pub use isolate::MessageCallback;
|
||||
pub use isolate::NearHeapLimitCallback;
|
||||
pub use isolate::OwnedIsolate;
|
||||
pub use isolate::PromiseRejectCallback;
|
||||
pub use isolate_create_params::CreateParams;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
extern crate lazy_static;
|
||||
|
||||
use std::convert::{Into, TryFrom, TryInto};
|
||||
use std::ffi::c_void;
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
@ -3313,3 +3314,41 @@ fn module_snapshot() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TestHeapLimitState {
|
||||
near_heap_limit_callback_calls: u64,
|
||||
}
|
||||
|
||||
extern "C" fn heap_limit_callback(
|
||||
data: *mut c_void,
|
||||
current_heap_limit: usize,
|
||||
_initial_heap_limit: usize,
|
||||
) -> usize {
|
||||
let state = unsafe { &mut *(data as *mut TestHeapLimitState) };
|
||||
state.near_heap_limit_callback_calls += 1;
|
||||
current_heap_limit * 2 // Avoid V8 OOM.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heap_limits() {
|
||||
let _setup_guard = setup();
|
||||
|
||||
let params = v8::CreateParams::default().heap_limits(0, 20 * 1024 * 1024); // 20 MB
|
||||
let isolate = &mut v8::Isolate::new(params);
|
||||
|
||||
let mut test_state = TestHeapLimitState::default();
|
||||
let state_ptr = &mut test_state as *mut _ as *mut c_void;
|
||||
isolate.add_near_heap_limit_callback(heap_limit_callback, state_ptr);
|
||||
|
||||
let scope = &mut v8::HandleScope::new(isolate);
|
||||
|
||||
// Allocate some strings; 20 MB is reached at about 800k iterations.
|
||||
for _ in 0..1_000_000 {
|
||||
v8::String::new(scope, "HelloWorld").unwrap();
|
||||
if test_state.near_heap_limit_callback_calls > 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_eq!(1, test_state.near_heap_limit_callback_calls);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue