Commit 55a01ec7 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by Commit Bot

Reland "[weakrefs] Schedule FinalizationGroup cleanup tasks from within V8"

Deprecate the following explicit FinalizationGroup APIs in favor of
automatic handling of FinalizationGroup cleanup callbacks:
  - v8::Isolate::SetHostCleanupFinalizationGroupCallback
  - v8::FinaliationGroup::Cleanup

If no HostCleanupFinalizationGroupCallback is set, then
FinalizationGroup cleanup callbacks are automatically scheduled by V8
itself as non-nestable foreground tasks.

When a Context being disposed, all FinalizationGroups that are
associated with it are removed from the dirty list, cancelling
scheduled cleanup.

This is a reland of 31d8ff7a

Bug: v8:8179, v8:10190
Change-Id: I704ecf48aeebac1dc2c05ea1c052f6a2560ae332
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2045723
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66208}
parent 841fd707
......@@ -2299,6 +2299,8 @@ v8_source_set("v8_base_without_compiler") {
"src/heap/factory-inl.h",
"src/heap/factory.cc",
"src/heap/factory.h",
"src/heap/finalization-group-cleanup-task.cc",
"src/heap/finalization-group-cleanup-task.h",
"src/heap/gc-idle-time-handler.cc",
"src/heap/gc-idle-time-handler.h",
"src/heap/gc-tracer.cc",
......
......@@ -5912,6 +5912,9 @@ class V8_EXPORT FinalizationGroup : public Object {
* occurred. Otherwise the result is |true| if the cleanup callback
* was called successfully. The result is never |false|.
*/
V8_DEPRECATED(
"FinalizationGroup cleanup is automatic if "
"HostCleanupFinalizationGroupCallback is not set")
static V8_WARN_UNUSED_RESULT Maybe<bool> Cleanup(
Local<FinalizationGroup> finalization_group);
};
......@@ -8481,6 +8484,9 @@ class V8_EXPORT Isolate {
* are ready to be cleaned up and require FinalizationGroup::Cleanup()
* to be called in a future task.
*/
V8_DEPRECATED(
"FinalizationGroup cleanup is automatic if "
"HostCleanupFinalizationGroupCallback is not set")
void SetHostCleanupFinalizationGroupCallback(
HostCleanupFinalizationGroupCallback callback);
......@@ -9085,10 +9091,10 @@ class V8_EXPORT Isolate {
void LowMemoryNotification();
/**
* Optional notification that a context has been disposed. V8 uses
* these notifications to guide the GC heuristic. Returns the number
* of context disposals - including this one - since the last time
* V8 had a chance to clean up.
* Optional notification that a context has been disposed. V8 uses these
* notifications to guide the GC heuristic and cancel FinalizationGroup
* cleanup tasks. Returns the number of context disposals - including this one
* - since the last time V8 had a chance to clean up.
*
* The optional parameter |dependant_context| specifies whether the disposed
* context was depending on state from other contexts or not.
......
......@@ -11033,6 +11033,26 @@ void InvokeFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
callback(info);
}
void InvokeFinalizationGroupCleanupFromTask(
Handle<Context> context, Handle<JSFinalizationGroup> finalization_group,
Handle<Object> callback) {
Isolate* isolate = finalization_group->native_context().GetIsolate();
RuntimeCallTimerScope timer(
isolate, RuntimeCallCounterId::kFinalizationGroupCleanupFromTask);
// Do not use ENTER_V8 because this is always called from a running
// FinalizationGroupCleanupTask within V8 and we should not log it as an API
// call. This method is implemented here to avoid duplication of the exception
// handling and microtask running logic in CallDepthScope.
if (IsExecutionTerminatingCheck(isolate)) return;
Local<v8::Context> api_context = Utils::ToLocal(context);
CallDepthScope<true> call_depth_scope(isolate, api_context);
VMState<OTHER> state(isolate);
if (JSFinalizationGroup::Cleanup(isolate, finalization_group, callback)
.IsNothing()) {
call_depth_scope.Escape();
}
}
// Undefine macros for jumbo build.
#undef LOG_API
#undef ENTER_V8_DO_NOT_USE
......
......@@ -26,6 +26,7 @@ namespace v8 {
namespace internal {
class JSArrayBufferView;
class JSFinalizationGroup;
} // namespace internal
namespace debug {
......@@ -561,6 +562,10 @@ void InvokeAccessorGetterCallback(
void InvokeFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
v8::FunctionCallback callback);
void InvokeFinalizationGroupCleanupFromTask(
Handle<Context> context, Handle<JSFinalizationGroup> finalization_group,
Handle<Object> callback);
} // namespace internal
} // namespace v8
......
......@@ -148,9 +148,8 @@ BUILTIN(FinalizationGroupCleanupSome) {
callback = callback_obj;
}
// Don't do set_scheduled_for_cleanup(false); we still have the microtask
// scheduled and don't want to schedule another one in case the user never
// executes microtasks.
// Don't do set_scheduled_for_cleanup(false); we still have the task
// scheduled.
if (JSFinalizationGroup::Cleanup(isolate, finalization_group, callback)
.IsNothing()) {
DCHECK(isolate->has_pending_exception());
......
......@@ -916,16 +916,6 @@ MaybeLocal<Promise> Shell::HostImportModuleDynamically(
return MaybeLocal<Promise>();
}
void Shell::HostCleanupFinalizationGroup(Local<Context> context,
Local<FinalizationGroup> fg) {
Isolate* isolate = context->GetIsolate();
PerIsolateData::Get(isolate)->HostCleanupFinalizationGroup(fg);
}
void PerIsolateData::HostCleanupFinalizationGroup(Local<FinalizationGroup> fg) {
cleanup_finalization_groups_.emplace(isolate_, fg);
}
void Shell::HostInitializeImportMetaObject(Local<Context> context,
Local<Module> module,
Local<Object> meta) {
......@@ -1123,15 +1113,6 @@ MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
return result;
}
MaybeLocal<FinalizationGroup> PerIsolateData::GetCleanupFinalizationGroup() {
if (cleanup_finalization_groups_.empty())
return MaybeLocal<FinalizationGroup>();
Local<FinalizationGroup> result =
cleanup_finalization_groups_.front().Get(isolate_);
cleanup_finalization_groups_.pop();
return result;
}
PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
data_->realm_count_ = 1;
data_->realm_current_ = 0;
......@@ -1281,8 +1262,11 @@ void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
int index) {
Isolate* isolate = args.GetIsolate();
PerIsolateData* data = PerIsolateData::Get(isolate);
DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
Local<Context> context = data->realms_[index].Get(isolate);
DisposeModuleEmbedderData(context);
data->realms_[index].Reset();
// ContextDisposedNotification expects the disposed context to be entered.
v8::Context::Scope scope(context);
isolate->ContextDisposedNotification();
isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
}
......@@ -2742,8 +2726,6 @@ void SourceGroup::ExecuteInThread() {
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
Isolate* isolate = Isolate::New(create_params);
isolate->SetHostCleanupFinalizationGroupCallback(
Shell::HostCleanupFinalizationGroup);
isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
......@@ -2889,8 +2871,6 @@ void Worker::ExecuteInThread() {
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
Isolate* isolate = Isolate::New(create_params);
isolate->SetHostCleanupFinalizationGroupCallback(
Shell::HostCleanupFinalizationGroup);
isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
......@@ -3272,21 +3252,6 @@ bool RunSetTimeoutCallback(Isolate* isolate, bool* did_run) {
return true;
}
bool RunCleanupFinalizationGroupCallback(Isolate* isolate, bool* did_run) {
PerIsolateData* data = PerIsolateData::Get(isolate);
HandleScope handle_scope(isolate);
while (true) {
Local<FinalizationGroup> fg;
if (!data->GetCleanupFinalizationGroup().ToLocal(&fg)) return true;
*did_run = true;
TryCatch try_catch(isolate);
try_catch.SetVerbose(true);
if (FinalizationGroup::Cleanup(fg).IsNothing()) {
return false;
}
}
}
bool ProcessMessages(
Isolate* isolate,
const std::function<platform::MessageLoopBehavior()>& behavior) {
......@@ -3303,17 +3268,12 @@ bool ProcessMessages(
v8::platform::RunIdleTasks(g_default_platform, isolate,
50.0 / base::Time::kMillisecondsPerSecond);
}
bool ran_finalization_callback = false;
if (!RunCleanupFinalizationGroupCallback(isolate,
&ran_finalization_callback)) {
return false;
}
bool ran_set_timeout = false;
if (!RunSetTimeoutCallback(isolate, &ran_set_timeout)) {
return false;
}
if (!ran_set_timeout && !ran_finalization_callback) return true;
if (!ran_set_timeout) return true;
}
return true;
}
......@@ -3768,8 +3728,6 @@ int Shell::Main(int argc, char* argv[]) {
}
Isolate* isolate = Isolate::New(create_params);
isolate->SetHostCleanupFinalizationGroupCallback(
Shell::HostCleanupFinalizationGroup);
isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
......@@ -3832,8 +3790,6 @@ int Shell::Main(int argc, char* argv[]) {
i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
Isolate* isolate2 = Isolate::New(create_params);
i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
isolate2->SetHostCleanupFinalizationGroupCallback(
Shell::HostCleanupFinalizationGroup);
isolate2->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate2->SetHostInitializeImportMetaObjectCallback(
......
......@@ -226,8 +226,6 @@ class PerIsolateData {
PerIsolateData* data_;
};
inline void HostCleanupFinalizationGroup(Local<FinalizationGroup> fg);
inline MaybeLocal<FinalizationGroup> GetCleanupFinalizationGroup();
inline void SetTimeout(Local<Function> callback, Local<Context> context);
inline MaybeLocal<Function> GetTimeoutCallback();
inline MaybeLocal<Context> GetTimeoutContext();
......@@ -245,7 +243,6 @@ class PerIsolateData {
Global<Value> realm_shared_;
std::queue<Global<Function>> set_timeout_callbacks_;
std::queue<Global<Context>> set_timeout_contexts_;
std::queue<Global<FinalizationGroup>> cleanup_finalization_groups_;
AsyncHooks* async_hooks_wrapper_;
int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args,
......@@ -423,8 +420,6 @@ class Shell : public i::AllStatic {
static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args);
static void MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
static void RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
static void HostCleanupFinalizationGroup(Local<Context> context,
Local<FinalizationGroup> fg);
static MaybeLocal<Promise> HostImportModuleDynamically(
Local<Context> context, Local<ScriptOrModule> referrer,
Local<String> specifier);
......
......@@ -1404,6 +1404,10 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
void ClearKeptObjects();
void SetHostCleanupFinalizationGroupCallback(
HostCleanupFinalizationGroupCallback callback);
HostCleanupFinalizationGroupCallback
host_cleanup_finalization_group_callback() const {
return host_cleanup_finalization_group_callback_;
}
void RunHostCleanupFinalizationGroupCallback(Handle<JSFinalizationGroup> fg);
void SetHostImportModuleDynamicallyCallback(
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/finalization-group-cleanup-task.h"
#include "src/execution/frames.h"
#include "src/execution/interrupts-scope.h"
#include "src/execution/stack-guard.h"
#include "src/execution/v8threads.h"
#include "src/heap/heap-inl.h"
#include "src/objects/js-weak-refs-inl.h"
#include "src/tracing/trace-event.h"
namespace v8 {
namespace internal {
FinalizationGroupCleanupTask::FinalizationGroupCleanupTask(Heap* heap)
: heap_(heap) {}
void FinalizationGroupCleanupTask::SlowAssertNoActiveJavaScript() {
#ifdef ENABLE_SLOW_DCHECKS
class NoActiveJavaScript : public ThreadVisitor {
public:
void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
DCHECK(!it.frame()->is_java_script());
}
}
};
NoActiveJavaScript no_active_js_visitor;
Isolate* isolate = heap_->isolate();
no_active_js_visitor.VisitThread(isolate, isolate->thread_local_top());
isolate->thread_manager()->IterateArchivedThreads(&no_active_js_visitor);
#endif // ENABLE_SLOW_DCHECKS
}
void FinalizationGroupCleanupTask::Run() {
Isolate* isolate = heap_->isolate();
DCHECK(!isolate->host_cleanup_finalization_group_callback());
SlowAssertNoActiveJavaScript();
TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8",
"V8.FinalizationGroupCleanupTask");
HandleScope handle_scope(isolate);
Handle<JSFinalizationGroup> finalization_group;
// There could be no dirty FinalizationGroups. When a context is disposed by
// the embedder, its FinalizationGroups are removed from the dirty list.
if (!heap_->TakeOneDirtyJSFinalizationGroup().ToHandle(&finalization_group)) {
return;
}
finalization_group->set_scheduled_for_cleanup(false);
// Since FinalizationGroup cleanup callbacks are scheduled by V8, enter the
// FinalizationGroup's context.
Handle<Context> context(Context::cast(finalization_group->native_context()),
isolate);
Handle<Object> callback(finalization_group->cleanup(), isolate);
v8::Context::Scope context_scope(v8::Utils::ToLocal(context));
v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
catcher.SetVerbose(true);
// Exceptions are reported via the message handler. This is ensured by the
// verbose TryCatch.
InvokeFinalizationGroupCleanupFromTask(context, finalization_group, callback);
// Repost if there are remaining dirty FinalizationGroups.
heap_->set_is_finalization_group_cleanup_task_posted(false);
heap_->PostFinalizationGroupCleanupTaskIfNeeded();
}
} // namespace internal
} // namespace v8
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_HEAP_FINALIZATION_GROUP_CLEANUP_TASK_H_
#define V8_HEAP_FINALIZATION_GROUP_CLEANUP_TASK_H_
#include "include/v8-platform.h"
#include "src/objects/js-weak-refs.h"
namespace v8 {
namespace internal {
// The GC schedules a cleanup task when the dirty FinalizationGroup list is
// non-empty. The task processes a single FinalizationGroup and posts another
// cleanup task if there are remaining dirty FinalizationGroups on the list.
class FinalizationGroupCleanupTask : public Task {
public:
explicit FinalizationGroupCleanupTask(Heap* heap);
~FinalizationGroupCleanupTask() override = default;
void Run() override;
private:
FinalizationGroupCleanupTask(const FinalizationGroupCleanupTask&) = delete;
void operator=(const FinalizationGroupCleanupTask&) = delete;
void SlowAssertNoActiveJavaScript();
Heap* heap_;
};
} // namespace internal
} // namespace v8
#endif // V8_HEAP_FINALIZATION_GROUP_CLEANUP_TASK_H_
......@@ -640,6 +640,10 @@ void Heap::DecrementExternalBackingStoreBytes(ExternalBackingStoreType type,
base::CheckedDecrement(&backing_store_bytes_, amount);
}
bool Heap::HasDirtyJSFinalizationGroups() {
return !dirty_js_finalization_groups().IsUndefined(isolate());
}
AlwaysAllocateScope::AlwaysAllocateScope(Heap* heap) : heap_(heap) {
heap_->always_allocate_scope_count_++;
}
......
......@@ -33,6 +33,7 @@
#include "src/heap/combined-heap.h"
#include "src/heap/concurrent-marking.h"
#include "src/heap/embedder-tracing.h"
#include "src/heap/finalization-group-cleanup-task.h"
#include "src/heap/gc-idle-time-handler.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/heap-controller.h"
......@@ -1197,17 +1198,11 @@ void Heap::GarbageCollectionEpilogue() {
ReduceNewSpaceSize();
}
if (FLAG_harmony_weak_refs) {
if (FLAG_harmony_weak_refs &&
isolate()->host_cleanup_finalization_group_callback()) {
HandleScope handle_scope(isolate());
while (!isolate()->heap()->dirty_js_finalization_groups().IsUndefined(
isolate())) {
Handle<JSFinalizationGroup> finalization_group(
JSFinalizationGroup::cast(
isolate()->heap()->dirty_js_finalization_groups()),
isolate());
isolate()->heap()->set_dirty_js_finalization_groups(
finalization_group->next());
finalization_group->set_next(ReadOnlyRoots(isolate()).undefined_value());
Handle<JSFinalizationGroup> finalization_group;
while (TakeOneDirtyJSFinalizationGroup().ToHandle(&finalization_group)) {
isolate()->RunHostCleanupFinalizationGroupCallback(finalization_group);
}
}
......@@ -1662,6 +1657,9 @@ int Heap::NotifyContextDisposed(bool dependant_context) {
memory_reducer_->NotifyPossibleGarbage(event);
}
isolate()->AbortConcurrentOptimization(BlockingBehavior::kDontBlock);
if (!isolate()->context().is_null()) {
RemoveDirtyFinalizationGroupsOnContext(isolate()->raw_native_context());
}
number_of_disposed_maps_ = retained_maps().length();
tracer()->AddContextDisposalTime(MonotonicallyIncreasingTimeInMs());
......@@ -6043,11 +6041,25 @@ void Heap::SetInterpreterEntryTrampolineForProfiling(Code code) {
set_interpreter_entry_trampoline_for_profiling(code);
}
void Heap::PostFinalizationGroupCleanupTaskIfNeeded() {
DCHECK(!isolate()->host_cleanup_finalization_group_callback());
// Only one cleanup task is posted at a time.
if (!HasDirtyJSFinalizationGroups() ||
is_finalization_group_cleanup_task_posted_) {
return;
}
auto taskrunner = V8::GetCurrentPlatform()->GetForegroundTaskRunner(
reinterpret_cast<v8::Isolate*>(isolate()));
auto task = std::make_unique<FinalizationGroupCleanupTask>(this);
taskrunner->PostNonNestableTask(std::move(task));
is_finalization_group_cleanup_task_posted_ = true;
}
void Heap::AddDirtyJSFinalizationGroup(
JSFinalizationGroup finalization_group,
std::function<void(HeapObject object, ObjectSlot slot, Object target)>
gc_notify_updated_slot) {
DCHECK(dirty_js_finalization_groups().IsUndefined(isolate()) ||
DCHECK(!HasDirtyJSFinalizationGroups() ||
dirty_js_finalization_groups().IsJSFinalizationGroup());
DCHECK(finalization_group.next().IsUndefined(isolate()));
DCHECK(!finalization_group.scheduled_for_cleanup());
......@@ -6062,6 +6074,44 @@ void Heap::AddDirtyJSFinalizationGroup(
// for the root pointing to the first JSFinalizationGroup.
}
MaybeHandle<JSFinalizationGroup> Heap::TakeOneDirtyJSFinalizationGroup() {
if (HasDirtyJSFinalizationGroups()) {
Handle<JSFinalizationGroup> finalization_group(
JSFinalizationGroup::cast(dirty_js_finalization_groups()), isolate());
set_dirty_js_finalization_groups(finalization_group->next());
finalization_group->set_next(ReadOnlyRoots(isolate()).undefined_value());
return finalization_group;
}
return {};
}
void Heap::RemoveDirtyFinalizationGroupsOnContext(NativeContext context) {
if (!FLAG_harmony_weak_refs) return;
if (isolate()->host_cleanup_finalization_group_callback()) return;
DisallowHeapAllocation no_gc;
Isolate* isolate = this->isolate();
Object prev = ReadOnlyRoots(isolate).undefined_value();
Object current = dirty_js_finalization_groups();
while (!current.IsUndefined(isolate)) {
JSFinalizationGroup finalization_group = JSFinalizationGroup::cast(current);
if (finalization_group.native_context() == context) {
if (prev.IsUndefined(isolate)) {
set_dirty_js_finalization_groups(finalization_group.next());
} else {
JSFinalizationGroup::cast(prev).set_next(finalization_group.next());
}
finalization_group.set_scheduled_for_cleanup(false);
current = finalization_group.next();
finalization_group.set_next(ReadOnlyRoots(isolate).undefined_value());
} else {
prev = current;
current = finalization_group.next();
}
}
}
void Heap::KeepDuringJob(Handle<JSReceiver> target) {
DCHECK(FLAG_harmony_weak_refs);
DCHECK(weak_refs_keep_during_job().IsUndefined() ||
......
......@@ -800,12 +800,33 @@ class Heap {
// See also: FLAG_interpreted_frames_native_stack.
void SetInterpreterEntryTrampolineForProfiling(Code code);
// Add finalization_group into the dirty_js_finalization_groups list.
// Add finalization_group to the end of the dirty_js_finalization_groups list.
void AddDirtyJSFinalizationGroup(
JSFinalizationGroup finalization_group,
std::function<void(HeapObject object, ObjectSlot slot, Object target)>
gc_notify_updated_slot);
// Pop and return the head of the dirty_js_finalization_groups list.
MaybeHandle<JSFinalizationGroup> TakeOneDirtyJSFinalizationGroup();
// Called from Heap::NotifyContextDisposed to remove all FinalizationGroups
// with {context} from the dirty list when the context e.g. navigates away or
// is detached. If the dirty list is empty afterwards, the cleanup task is
// aborted if needed.
void RemoveDirtyFinalizationGroupsOnContext(NativeContext context);
inline bool HasDirtyJSFinalizationGroups();
void PostFinalizationGroupCleanupTaskIfNeeded();
void set_is_finalization_group_cleanup_task_posted(bool posted) {
is_finalization_group_cleanup_task_posted_ = posted;
}
bool is_finalization_group_cleanup_task_posted() {
return is_finalization_group_cleanup_task_posted_;
}
V8_EXPORT_PRIVATE void KeepDuringJob(Handle<JSReceiver> target);
void ClearKeptObjects();
......@@ -2156,6 +2177,8 @@ class Heap {
std::vector<HeapObjectAllocationTracker*> allocation_trackers_;
bool is_finalization_group_cleanup_task_posted_ = false;
std::unique_ptr<third_party_heap::Heap> tp_heap_;
// Classes in "heap" can be friends.
......
......@@ -2534,6 +2534,9 @@ void MarkCompactCollector::ClearJSWeakRefs() {
RecordSlot(weak_cell, slot, HeapObject::cast(*slot));
}
}
if (!isolate()->host_cleanup_finalization_group_callback()) {
heap()->PostFinalizationGroupCleanupTaskIfNeeded();
}
}
void MarkCompactCollector::AbortWeakObjects() {
......
......@@ -989,6 +989,7 @@ class RuntimeCallTimer final {
V(DeoptimizeCode) \
V(DeserializeContext) \
V(DeserializeIsolate) \
V(FinalizationGroupCleanupFromTask) \
V(FunctionCallback) \
V(FunctionLengthGetter) \
V(FunctionPrototypeGetter) \
......
......@@ -6,7 +6,6 @@
#define V8_OBJECTS_JS_WEAK_REFS_H_
#include "src/objects/js-objects.h"
#include "src/objects/microtask.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
// Flags: --no-stress-opt
// Since cleanup tasks are top-level tasks, errors thrown from them don't stop
// future cleanup tasks from running.
function callback(iter) {
[...iter];
throw new Error('callback');
};
const fg1 = new FinalizationGroup(callback);
const fg2 = new FinalizationGroup(callback);
(function() {
let x = {};
fg1.register(x, {});
fg2.register(x, {});
x = null;
})();
gc();
*%(basename)s:{NUMBER}: Error: callback
throw new Error('callback');
^
Error: callback
at callback (*%(basename)s:{NUMBER}:{NUMBER})
*%(basename)s:{NUMBER}: Error: callback
throw new Error('callback');
^
Error: callback
at callback (*%(basename)s:{NUMBER}:{NUMBER})
......@@ -9,15 +9,28 @@ let r = Realm.create();
let FG = Realm.eval(r, "FinalizationGroup");
Realm.detachGlobal(r);
let fg_not_run = new FG(() => {
assertUnreachable();
});
(() => {
fg_not_run.register({});
})();
gc();
// Disposing the realm cancels the already scheduled fg_not_run's finalizer.
Realm.dispose(r);
let fg = new FG(()=> {
cleanedUp = true;
});
// FGs that are alive after disposal can still schedule tasks.
(() => {
let object = {};
fg.register(object, {});
// object goes out of scope.
// object becomes unreachable.
})();
gc();
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking
let call_count = 0;
let reentrant_gc =
function(iter) {
[...iter];
gc();
call_count++;
}
let fg = new FinalizationGroup(reentrant_gc);
(function() {
fg.register({}, 42);
})();
gc();
setTimeout(function() {
assertEquals(1, call_count);
}, 0);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment