diff --git a/common.gypi b/common.gypi index 33c14546410f3c..cae7c10a647237 100644 --- a/common.gypi +++ b/common.gypi @@ -35,7 +35,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.21', + 'v8_embedder_string': '-node.22', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 0ffa2b794df744..13bf2b0fc344b4 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -2298,6 +2298,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", diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 0a0ae9aab96f30..10b444e862f74e 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -5885,6 +5885,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 Cleanup( Local finalization_group); }; @@ -8444,6 +8447,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); @@ -9056,10 +9062,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. diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index 78a1e07f540eff..be13cbf397db23 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -10950,6 +10950,26 @@ void InvokeFunctionCallback(const v8::FunctionCallbackInfo& info, callback(info); } +void InvokeFinalizationGroupCleanupFromTask( + Handle context, Handle finalization_group, + Handle 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 api_context = Utils::ToLocal(context); + CallDepthScope call_depth_scope(isolate, api_context); + VMState 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 diff --git a/deps/v8/src/api/api.h b/deps/v8/src/api/api.h index 004740099cb78e..b39cf1ce275777 100644 --- a/deps/v8/src/api/api.h +++ b/deps/v8/src/api/api.h @@ -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& info, v8::FunctionCallback callback); +void InvokeFinalizationGroupCleanupFromTask( + Handle context, Handle finalization_group, + Handle callback); + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/builtins/builtins-weak-refs.cc b/deps/v8/src/builtins/builtins-weak-refs.cc index 28fb9c9cbdfaf4..5a7ae6ca870471 100644 --- a/deps/v8/src/builtins/builtins-weak-refs.cc +++ b/deps/v8/src/builtins/builtins-weak-refs.cc @@ -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()); diff --git a/deps/v8/src/d8/d8.cc b/deps/v8/src/d8/d8.cc index 4337fe4bc15c96..3bf129a8bd9c23 100644 --- a/deps/v8/src/d8/d8.cc +++ b/deps/v8/src/d8/d8.cc @@ -916,16 +916,6 @@ MaybeLocal Shell::HostImportModuleDynamically( return MaybeLocal(); } -void Shell::HostCleanupFinalizationGroup(Local context, - Local fg) { - Isolate* isolate = context->GetIsolate(); - PerIsolateData::Get(isolate)->HostCleanupFinalizationGroup(fg); -} - -void PerIsolateData::HostCleanupFinalizationGroup(Local fg) { - cleanup_finalization_groups_.emplace(isolate_, fg); -} - void Shell::HostInitializeImportMetaObject(Local context, Local module, Local meta) { @@ -1123,15 +1113,6 @@ MaybeLocal PerIsolateData::GetTimeoutContext() { return result; } -MaybeLocal PerIsolateData::GetCleanupFinalizationGroup() { - if (cleanup_finalization_groups_.empty()) - return MaybeLocal(); - Local 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& args, int index) { Isolate* isolate = args.GetIsolate(); PerIsolateData* data = PerIsolateData::Get(isolate); - DisposeModuleEmbedderData(data->realms_[index].Get(isolate)); + Local 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 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& behavior) { @@ -3302,17 +3267,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; } @@ -3767,8 +3727,6 @@ int Shell::Main(int argc, char* argv[]) { } Isolate* isolate = Isolate::New(create_params); - isolate->SetHostCleanupFinalizationGroupCallback( - Shell::HostCleanupFinalizationGroup); isolate->SetHostImportModuleDynamicallyCallback( Shell::HostImportModuleDynamically); isolate->SetHostInitializeImportMetaObjectCallback( @@ -3831,8 +3789,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( diff --git a/deps/v8/src/d8/d8.h b/deps/v8/src/d8/d8.h index 435eb6dbd8a966..4420327b844c7c 100644 --- a/deps/v8/src/d8/d8.h +++ b/deps/v8/src/d8/d8.h @@ -226,8 +226,6 @@ class PerIsolateData { PerIsolateData* data_; }; - inline void HostCleanupFinalizationGroup(Local fg); - inline MaybeLocal GetCleanupFinalizationGroup(); inline void SetTimeout(Local callback, Local context); inline MaybeLocal GetTimeoutCallback(); inline MaybeLocal GetTimeoutContext(); @@ -245,7 +243,6 @@ class PerIsolateData { Global realm_shared_; std::queue> set_timeout_callbacks_; std::queue> set_timeout_contexts_; - std::queue> cleanup_finalization_groups_; AsyncHooks* async_hooks_wrapper_; int RealmIndexOrThrow(const v8::FunctionCallbackInfo& args, @@ -423,8 +420,6 @@ class Shell : public i::AllStatic { static void SetUMask(const v8::FunctionCallbackInfo& args); static void MakeDirectory(const v8::FunctionCallbackInfo& args); static void RemoveDirectory(const v8::FunctionCallbackInfo& args); - static void HostCleanupFinalizationGroup(Local context, - Local fg); static MaybeLocal HostImportModuleDynamically( Local context, Local referrer, Local specifier); diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h index 913249dfe596f0..48bd7b74227033 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -1391,6 +1391,10 @@ class 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 fg); void SetHostImportModuleDynamicallyCallback( diff --git a/deps/v8/src/heap/finalization-group-cleanup-task.cc b/deps/v8/src/heap/finalization-group-cleanup-task.cc new file mode 100644 index 00000000000000..f0bf6726309a2e --- /dev/null +++ b/deps/v8/src/heap/finalization-group-cleanup-task.cc @@ -0,0 +1,74 @@ +// 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 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::cast(finalization_group->native_context()), + isolate); + Handle callback(finalization_group->cleanup(), isolate); + v8::Context::Scope context_scope(v8::Utils::ToLocal(context)); + v8::TryCatch catcher(reinterpret_cast(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 diff --git a/deps/v8/src/heap/finalization-group-cleanup-task.h b/deps/v8/src/heap/finalization-group-cleanup-task.h new file mode 100644 index 00000000000000..ae15dd883ed9e7 --- /dev/null +++ b/deps/v8/src/heap/finalization-group-cleanup-task.h @@ -0,0 +1,36 @@ +// 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_ diff --git a/deps/v8/src/heap/heap-inl.h b/deps/v8/src/heap/heap-inl.h index 0da0b35d392d8a..4253854f836261 100644 --- a/deps/v8/src/heap/heap-inl.h +++ b/deps/v8/src/heap/heap-inl.h @@ -615,6 +615,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_++; } diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index 7e11c8626027d9..1b644cebb6bc86 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -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 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 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()); @@ -6002,11 +6000,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(isolate())); + auto task = std::make_unique(this); + taskrunner->PostNonNestableTask(std::move(task)); + is_finalization_group_cleanup_task_posted_ = true; +} + void Heap::AddDirtyJSFinalizationGroup( JSFinalizationGroup finalization_group, std::function 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()); @@ -6021,6 +6033,44 @@ void Heap::AddDirtyJSFinalizationGroup( // for the root pointing to the first JSFinalizationGroup. } +MaybeHandle Heap::TakeOneDirtyJSFinalizationGroup() { + if (HasDirtyJSFinalizationGroups()) { + Handle 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 target) { DCHECK(FLAG_harmony_weak_refs); DCHECK(weak_refs_keep_during_job().IsUndefined() || diff --git a/deps/v8/src/heap/heap.h b/deps/v8/src/heap/heap.h index 86dbab60a7a070..58bb841767b724 100644 --- a/deps/v8/src/heap/heap.h +++ b/deps/v8/src/heap/heap.h @@ -795,12 +795,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 gc_notify_updated_slot); + // Pop and return the head of the dirty_js_finalization_groups list. + MaybeHandle 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 target); void ClearKeptObjects(); @@ -2149,6 +2170,8 @@ class Heap { std::vector allocation_trackers_; + bool is_finalization_group_cleanup_task_posted_ = false; + std::unique_ptr tp_heap_; // Classes in "heap" can be friends. diff --git a/deps/v8/src/heap/mark-compact.cc b/deps/v8/src/heap/mark-compact.cc index 2113f89ba67b51..d08f8a111b5bbd 100644 --- a/deps/v8/src/heap/mark-compact.cc +++ b/deps/v8/src/heap/mark-compact.cc @@ -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() { diff --git a/deps/v8/src/logging/counters.h b/deps/v8/src/logging/counters.h index a2c918e4c13db2..240fee16ffd74c 100644 --- a/deps/v8/src/logging/counters.h +++ b/deps/v8/src/logging/counters.h @@ -988,6 +988,7 @@ class RuntimeCallTimer final { V(DeoptimizeCode) \ V(DeserializeContext) \ V(DeserializeIsolate) \ + V(FinalizationGroupCleanupFromTask) \ V(FunctionCallback) \ V(FunctionLengthGetter) \ V(FunctionPrototypeGetter) \ diff --git a/deps/v8/src/objects/js-weak-refs.h b/deps/v8/src/objects/js-weak-refs.h index 0b18240e707a68..021befe8b7c65a 100644 --- a/deps/v8/src/objects/js-weak-refs.h +++ b/deps/v8/src/objects/js-weak-refs.h @@ -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" diff --git a/deps/v8/test/message/weakref-finalizationgroup-error.js b/deps/v8/test/message/weakref-finalizationgroup-error.js new file mode 100644 index 00000000000000..045892187bae22 --- /dev/null +++ b/deps/v8/test/message/weakref-finalizationgroup-error.js @@ -0,0 +1,26 @@ +// 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(); diff --git a/deps/v8/test/message/weakref-finalizationgroup-error.out b/deps/v8/test/message/weakref-finalizationgroup-error.out new file mode 100644 index 00000000000000..21d4a10ef2c579 --- /dev/null +++ b/deps/v8/test/message/weakref-finalizationgroup-error.out @@ -0,0 +1,11 @@ +*%(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}) diff --git a/deps/v8/test/mjsunit/harmony/weakrefs/cleanup-on-detached-realm.js b/deps/v8/test/mjsunit/harmony/weakrefs/cleanup-on-detached-realm.js index 9cc548920c3ce0..5580e962d5a88c 100644 --- a/deps/v8/test/mjsunit/harmony/weakrefs/cleanup-on-detached-realm.js +++ b/deps/v8/test/mjsunit/harmony/weakrefs/cleanup-on-detached-realm.js @@ -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(); diff --git a/deps/v8/test/mjsunit/harmony/weakrefs/reentrant-task-abort-from-cleanup.js b/deps/v8/test/mjsunit/harmony/weakrefs/reentrant-task-abort-from-cleanup.js new file mode 100644 index 00000000000000..f4ad5bdf905fc2 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/weakrefs/reentrant-task-abort-from-cleanup.js @@ -0,0 +1,25 @@ +// 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);