0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2025-03-09 05:27:08 -04:00

feat: Add v8::Isolate::request_garbage_collection_for_testing (#1148)

This commit is contained in:
Bartek Iwańczuk 2022-12-02 21:10:37 +01:00 committed by GitHub
parent 0f43fc0b58
commit 3e8a572ceb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 38 deletions

View file

@ -317,6 +317,11 @@ bool v8__Isolate__HasPendingBackgroundTasks(v8::Isolate* isolate) {
return isolate->HasPendingBackgroundTasks();
}
void v8__Isolate__RequestGarbageCollectionForTesting(
v8::Isolate* isolate, v8::Isolate::GarbageCollectionType type) {
isolate->RequestGarbageCollectionForTesting(type);
}
void v8__Isolate__CreateParams__CONSTRUCT(
uninit_t<v8::Isolate::CreateParams>* buf) {
construct_in_place<v8::Isolate::CreateParams>(buf);

View file

@ -107,6 +107,15 @@ pub enum PromiseHookType {
After,
}
/// Types of garbage collections that can be requested via
/// [`Isolate::request_garbage_collection_for_testing`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum GarbageCollectionType {
Full,
Minor,
}
pub type MessageCallback = extern "C" fn(Local<Message>, Local<Value>);
pub type PromiseHook =
@ -471,6 +480,10 @@ extern "C" {
callback: extern "C" fn(*const FunctionCallbackInfo),
);
fn v8__Isolate__HasPendingBackgroundTasks(isolate: *const Isolate) -> bool;
fn v8__Isolate__RequestGarbageCollectionForTesting(
isolate: *mut Isolate,
r#type: usize,
);
fn v8__HeapProfiler__TakeHeapSnapshot(
isolate: *mut Isolate,
@ -1140,6 +1153,31 @@ impl Isolate {
unsafe { v8__Isolate__HasPendingBackgroundTasks(self) }
}
/// Request garbage collection with a specific embedderstack state in this
/// Isolate. It is only valid to call this function if --expose_gc was
/// specified.
///
/// This should only be used for testing purposes and not to enforce a garbage
/// collection schedule. It has strong negative impact on the garbage
/// collection performance. Use IdleNotificationDeadline() or
/// LowMemoryNotification() instead to influence the garbage collection
/// schedule.
#[inline(always)]
pub fn request_garbage_collection_for_testing(
&mut self,
r#type: GarbageCollectionType,
) {
unsafe {
v8__Isolate__RequestGarbageCollectionForTesting(
self,
match r#type {
GarbageCollectionType::Full => 0,
GarbageCollectionType::Minor => 1,
},
)
}
}
unsafe fn clear_scope_and_annex(&mut self) {
// Drop the scope stack.
ScopeData::drop_root(self);

View file

@ -97,6 +97,7 @@ pub use handle::Global;
pub use handle::Handle;
pub use handle::Local;
pub use handle::Weak;
pub use isolate::GarbageCollectionType;
pub use isolate::HeapStatistics;
pub use isolate::HostCreateShadowRealmContextCallback;
pub use isolate::HostImportModuleDynamicallyCallback;

View file

@ -161,8 +161,7 @@ fn global_from_into_raw() {
(raw, weak)
};
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(!weak.is_empty());
{
@ -172,8 +171,7 @@ fn global_from_into_raw() {
assert_eq!(global_from_weak, reconstructed);
}
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak.is_empty());
}
@ -6379,23 +6377,24 @@ fn clear_kept_objects() {
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
let step1 = r#"
var weakrefs = [];
for (let i = 0; i < 424242; i++) weakrefs.push(new WeakRef({ i }));
gc();
"#;
let step2 = r#"
if (weakrefs.some(w => !w.deref())) throw "fail";
"#;
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
let step2 = r#"
gc();
let step3 = r#"
if (weakrefs.every(w => w.deref())) throw "fail";
"#;
eval(scope, step1).unwrap();
scope.clear_kept_objects();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
eval(scope, step2).unwrap();
scope.clear_kept_objects();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
eval(scope, step3).unwrap();
}
#[test]
@ -7420,8 +7419,7 @@ fn weak_handle() {
let scope = &mut v8::HandleScope::new(scope);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak.is_empty());
assert_eq!(weak.to_local(scope), None);
@ -7450,8 +7448,8 @@ fn finalizers() {
}
let scope = &mut v8::HandleScope::new(scope);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
}
let finalizer_called = Rc::new(Cell::new(false));
@ -7486,8 +7484,7 @@ fn finalizers() {
};
let scope = &mut v8::HandleScope::new(scope);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak.is_empty());
assert!(finalizer_called.get());
}
@ -7521,8 +7518,8 @@ fn guaranteed_finalizers() {
}
let scope = &mut v8::HandleScope::new(scope);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
}
let finalizer_called = Rc::new(Cell::new(false));
@ -7557,8 +7554,7 @@ fn guaranteed_finalizers() {
};
let scope = &mut v8::HandleScope::new(scope);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak.is_empty());
assert!(finalizer_called.get());
}
@ -7583,8 +7579,7 @@ fn weak_from_global() {
assert_eq!(weak.to_global(scope).unwrap(), global);
drop(global);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak.is_empty());
}
@ -7633,8 +7628,8 @@ fn weak_from_into_raw() {
assert!(!finalizer_called.get());
(weak1, weak2)
};
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak1.is_empty());
assert!(weak2.is_empty());
assert!(finalizer_called.get());
@ -7648,8 +7643,8 @@ fn weak_from_into_raw() {
v8::Weak::new(scope, local)
};
assert!(!weak.is_empty());
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(weak.is_empty());
assert_eq!(weak.into_raw(), None);
}
@ -7680,8 +7675,8 @@ fn weak_from_into_raw() {
let raw2 = weak_with_finalizer.into_raw();
assert!(raw1.is_some());
assert!(raw2.is_some());
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(finalizer_called.get());
let weak1 = unsafe { v8::Weak::from_raw(scope, raw1) };
let weak2 = unsafe { v8::Weak::from_raw(scope, raw2) };
@ -7695,11 +7690,10 @@ fn weak_from_into_raw() {
let local = v8::Object::new(scope);
v8::Weak::new(scope, local).into_raw();
v8::Weak::with_finalizer(scope, local, Box::new(|_| {})).into_raw();
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
}
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
}
#[test]
@ -7739,8 +7733,7 @@ fn drop_weak_from_raw_in_finalizer() {
}
assert!(!finalized.get());
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert!(finalized.get());
}
@ -8747,8 +8740,8 @@ fn gc_callbacks() {
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
assert_eq!(state.mark_sweep_calls, 1);
assert_eq!(state.incremental_marking_calls, 0);
}
@ -8760,8 +8753,8 @@ fn gc_callbacks() {
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
// TODO use binding to Isolate::RequestGarbageCollectionForTesting instead of gc()
eval(scope, "gc()").unwrap();
scope
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
// Assert callback was removed and not called again.
assert_eq!(state.mark_sweep_calls, 1);
assert_eq!(state.incremental_marking_calls, 0);