1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/finalization-registry-cleanup-task.h"
6
7 #include "src/execution/frames.h"
8 #include "src/execution/interrupts-scope.h"
9 #include "src/execution/stack-guard.h"
10 #include "src/execution/v8threads.h"
11 #include "src/heap/heap-inl.h"
12 #include "src/objects/js-weak-refs-inl.h"
13 #include "src/tracing/trace-event.h"
14
15 namespace v8 {
16 namespace internal {
17
FinalizationRegistryCleanupTask(Heap * heap)18 FinalizationRegistryCleanupTask::FinalizationRegistryCleanupTask(Heap* heap)
19 : CancelableTask(heap->isolate()), heap_(heap) {}
20
SlowAssertNoActiveJavaScript()21 void FinalizationRegistryCleanupTask::SlowAssertNoActiveJavaScript() {
22 #ifdef ENABLE_SLOW_DCHECKS
23 class NoActiveJavaScript : public ThreadVisitor {
24 public:
25 void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
26 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
27 DCHECK(!it.frame()->is_java_script());
28 }
29 }
30 };
31 NoActiveJavaScript no_active_js_visitor;
32 Isolate* isolate = heap_->isolate();
33 no_active_js_visitor.VisitThread(isolate, isolate->thread_local_top());
34 isolate->thread_manager()->IterateArchivedThreads(&no_active_js_visitor);
35 #endif // ENABLE_SLOW_DCHECKS
36 }
37
RunInternal()38 void FinalizationRegistryCleanupTask::RunInternal() {
39 Isolate* isolate = heap_->isolate();
40 SlowAssertNoActiveJavaScript();
41
42 TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8",
43 "V8.FinalizationRegistryCleanupTask");
44
45 HandleScope handle_scope(isolate);
46 Handle<JSFinalizationRegistry> finalization_registry;
47 // There could be no dirty FinalizationRegistries. When a context is disposed
48 // by the embedder, its FinalizationRegistries are removed from the dirty
49 // list.
50 if (!heap_->DequeueDirtyJSFinalizationRegistry().ToHandle(
51 &finalization_registry)) {
52 return;
53 }
54 finalization_registry->set_scheduled_for_cleanup(false);
55
56 // Since FinalizationRegistry cleanup callbacks are scheduled by V8, enter the
57 // FinalizationRegistry's context.
58 Handle<Context> context(
59 Context::cast(finalization_registry->native_context()), isolate);
60 Handle<Object> callback(finalization_registry->cleanup(), isolate);
61 v8::Context::Scope context_scope(v8::Utils::ToLocal(context));
62 v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
63 catcher.SetVerbose(true);
64
65 // Exceptions are reported via the message handler. This is ensured by the
66 // verbose TryCatch.
67 //
68 // Cleanup is interrupted if there is an exception. The HTML spec calls for a
69 // microtask checkpoint after each cleanup task, so the task should return
70 // after an exception so the host can perform a microtask checkpoint. In case
71 // of exception, check if the FinalizationRegistry still needs cleanup
72 // and should be requeued.
73 //
74 // TODO(syg): Implement better scheduling for finalizers.
75 InvokeFinalizationRegistryCleanupFromTask(context, finalization_registry,
76 callback);
77 if (finalization_registry->NeedsCleanup() &&
78 !finalization_registry->scheduled_for_cleanup()) {
79 auto nop = [](HeapObject, ObjectSlot, Object) {};
80 heap_->EnqueueDirtyJSFinalizationRegistry(*finalization_registry, nop);
81 }
82
83 // Repost if there are remaining dirty FinalizationRegistries.
84 heap_->set_is_finalization_registry_cleanup_task_posted(false);
85 heap_->PostFinalizationRegistryCleanupTaskIfNeeded();
86 }
87
88 } // namespace internal
89 } // namespace v8
90