blob: 18222e783d17b5c4f098f33a6b3e839c795fb6bf [file] [log] [blame]
// 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-registry-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 {
FinalizationRegistryCleanupTask::FinalizationRegistryCleanupTask(Heap* heap)
: CancelableTask(heap->isolate()), heap_(heap) {}
void FinalizationRegistryCleanupTask::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 FinalizationRegistryCleanupTask::RunInternal() {
Isolate* isolate = heap_->isolate();
SlowAssertNoActiveJavaScript();
TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8",
"V8.FinalizationRegistryCleanupTask");
HandleScope handle_scope(isolate);
Handle<JSFinalizationRegistry> finalization_registry;
// There could be no dirty FinalizationRegistries. When a context is disposed
// by the embedder, its FinalizationRegistries are removed from the dirty
// list.
if (!heap_->DequeueDirtyJSFinalizationRegistry().ToHandle(
&finalization_registry)) {
return;
}
finalization_registry->set_scheduled_for_cleanup(false);
// Since FinalizationRegistry cleanup callbacks are scheduled by V8, enter the
// FinalizationRegistry's context.
Handle<Context> context(
Context::cast(finalization_registry->native_context()), isolate);
Handle<Object> callback(finalization_registry->cleanup(), isolate);
v8::Context::Scope context_scope(v8::Utils::ToLocal(context));
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8::TryCatch catcher(v8_isolate);
catcher.SetVerbose(true);
std::unique_ptr<MicrotasksScope> microtasks_scope;
MicrotaskQueue* microtask_queue =
finalization_registry->native_context().microtask_queue();
if (!microtask_queue) microtask_queue = isolate->default_microtask_queue();
if (microtask_queue &&
microtask_queue->microtasks_policy() == v8::MicrotasksPolicy::kScoped) {
// InvokeFinalizationRegistryCleanupFromTask will call into V8 API methods,
// so we need a valid microtasks scope on the stack to avoid running into
// the CallDepthScope check.
microtasks_scope.reset(new v8::MicrotasksScope(
v8_isolate, microtask_queue, v8::MicrotasksScope::kDoNotRunMicrotasks));
}
// Exceptions are reported via the message handler. This is ensured by the
// verbose TryCatch.
//
// Cleanup is interrupted if there is an exception. The HTML spec calls for a
// microtask checkpoint after each cleanup task, so the task should return
// after an exception so the host can perform a microtask checkpoint. In case
// of exception, check if the FinalizationRegistry still needs cleanup
// and should be requeued.
//
// TODO(syg): Implement better scheduling for finalizers.
InvokeFinalizationRegistryCleanupFromTask(context, finalization_registry,
callback);
if (finalization_registry->NeedsCleanup() &&
!finalization_registry->scheduled_for_cleanup()) {
auto nop = [](HeapObject, ObjectSlot, Object) {};
heap_->EnqueueDirtyJSFinalizationRegistry(*finalization_registry, nop);
}
// Repost if there are remaining dirty FinalizationRegistries.
heap_->set_is_finalization_registry_cleanup_task_posted(false);
heap_->PostFinalizationRegistryCleanupTaskIfNeeded();
}
} // namespace internal
} // namespace v8