Commit cb92ed09 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

[api, heap] Remove resurrecting finalizers

Removes V8-internal support for resurrecting finalizers in the garbage
collector.

The APIs have already been removed in http://crrev.com/c/3596174

Bug: v8:12672
Change-Id: Ia507e74659b61a2c8c08281d7f395aee51e3fe17
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3584115Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80078}
parent c0a63243
......@@ -40,6 +40,19 @@ constexpr size_t kBlockSize = 256;
} // namespace
enum WeaknessType {
// In the following cases, the embedder gets the parameter they passed in
// earlier, and 0 or 2 first embedder fields. Note that the internal
// fields must contain aligned non-V8 pointers. Getting pointers to V8
// objects through this interface would be GC unsafe so in that case the
// embedder gets a null pointer instead.
PHANTOM_WEAK,
PHANTOM_WEAK_2_EMBEDDER_FIELDS,
// The handle is automatically reset by the garbage collector when
// the object is no longer reachable.
PHANTOM_WEAK_RESET_HANDLE
};
template <class _NodeType>
class GlobalHandles::NodeBlock final {
public:
......@@ -434,7 +447,7 @@ void ExtractInternalFields(JSObject jsobject, void** embedder_fields, int len) {
class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
public:
// State transition diagram:
// FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE }
// FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> FREE
enum State {
FREE = 0,
NORMAL, // Normal global handle.
......@@ -488,33 +501,11 @@ class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
return weakness_type() == PHANTOM_WEAK_RESET_HANDLE;
}
bool IsFinalizerHandle() const { return weakness_type() == FINALIZER_WEAK; }
bool IsPendingPhantomCallback() const {
return state() == PENDING && IsPhantomCallback();
}
bool IsPendingPhantomResetHandle() const {
return state() == PENDING && IsPhantomResetHandle();
}
bool IsPendingFinalizer() const {
return state() == PENDING && weakness_type() == FINALIZER_WEAK;
}
bool IsPending() const { return state() == PENDING; }
bool IsRetainer() const {
return state() != FREE &&
!(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK);
}
bool IsRetainer() const { return state() != FREE && state() != NEAR_DEATH; }
bool IsStrongRetainer() const { return state() == NORMAL; }
bool IsWeakRetainer() const {
return state() == WEAK || state() == PENDING ||
(state() == NEAR_DEATH && weakness_type() == FINALIZER_WEAK);
}
bool IsWeakRetainer() const { return state() == WEAK || state() == PENDING; }
void MarkPending() {
DCHECK(state() == WEAK);
......@@ -604,30 +595,6 @@ class GlobalHandles::Node final : public NodeBase<GlobalHandles::Node> {
NodeSpace<Node>::Release(this);
}
void PostGarbageCollectionProcessing(Isolate* isolate) {
// This method invokes a finalizer. Updating the method name would require
// adjusting CFI blocklist as weak_callback_ is invoked on the wrong type.
CHECK(IsPendingFinalizer());
set_state(NEAR_DEATH);
// Check that we are not passing a finalized external string to
// the callback.
DCHECK(!object().IsExternalOneByteString() ||
ExternalOneByteString::cast(object()).resource() != nullptr);
DCHECK(!object().IsExternalTwoByteString() ||
ExternalTwoByteString::cast(object()).resource() != nullptr);
// Leaving V8.
VMState<EXTERNAL> vmstate(isolate);
HandleScope handle_scope(isolate);
void* embedder_fields[v8::kEmbedderFieldsInWeakCallback] = {nullptr,
nullptr};
v8::WeakCallbackInfo<void> data(reinterpret_cast<v8::Isolate*>(isolate),
parameter(), embedder_fields, nullptr);
weak_callback_(data);
// For finalizers the handle must have either been reset or made strong.
// Both cases reset the state.
CHECK_NE(NEAR_DEATH, state());
}
void MarkAsFree() { set_state(FREE); }
void MarkAsUsed() { set_state(NORMAL); }
......@@ -1034,10 +1001,7 @@ void GlobalHandles::MoveGlobal(Address** from, Address** to) {
if (node->IsWeak() && node->IsPhantomResetHandle()) {
node->set_parameter(to);
}
// - Strong handles do not require fixups.
// - Weak handles with finalizers and callbacks are too general to fix up. For
// those the callers need to ensure consistency.
// Strong handles do not require fixups.
}
void GlobalHandles::MoveTracedReference(Address** from, Address** to) {
......@@ -1190,19 +1154,6 @@ bool GlobalHandles::IsWeak(Address* location) {
return Node::FromLocation(location)->IsWeak();
}
DISABLE_CFI_PERF
void GlobalHandles::IterateWeakRootsForFinalizers(RootVisitor* v) {
for (Node* node : *regular_nodes_) {
if (node->IsWeakRetainer() && node->state() == Node::PENDING) {
DCHECK(!node->IsPhantomCallback());
DCHECK(!node->IsPhantomResetHandle());
// Finalizers need to survive.
v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
}
}
}
DISABLE_CFI_PERF
void GlobalHandles::IterateWeakRootsForPhantomHandles(
WeakSlotCallbackWithHeap should_reset_handle) {
......@@ -1240,18 +1191,6 @@ void GlobalHandles::IterateWeakRootsForPhantomHandles(
}
}
void GlobalHandles::IterateWeakRootsIdentifyFinalizers(
WeakSlotCallbackWithHeap should_reset_handle) {
for (Node* node : *regular_nodes_) {
if (node->IsWeak() &&
should_reset_handle(isolate()->heap(), node->location())) {
if (node->IsFinalizerHandle()) {
node->MarkPending();
}
}
}
}
void GlobalHandles::IdentifyWeakUnmodifiedObjects(
WeakSlotCallback is_unmodified) {
if (!FLAG_reclaim_unmodified_wrappers) return;
......@@ -1287,31 +1226,6 @@ void GlobalHandles::IterateYoungStrongAndDependentRoots(RootVisitor* v) {
}
}
void GlobalHandles::MarkYoungWeakDeadObjectsPending(
WeakSlotCallbackWithHeap is_dead) {
for (Node* node : young_nodes_) {
DCHECK(node->is_in_young_list());
if (node->IsWeak() && is_dead(isolate_->heap(), node->location())) {
if (!node->IsPhantomCallback() && !node->IsPhantomResetHandle()) {
node->MarkPending();
}
}
}
}
void GlobalHandles::IterateYoungWeakDeadObjectsForFinalizers(RootVisitor* v) {
for (Node* node : young_nodes_) {
DCHECK(node->is_in_young_list());
if (node->IsWeakRetainer() && (node->state() == Node::PENDING)) {
DCHECK(!node->IsPhantomCallback());
DCHECK(!node->IsPhantomResetHandle());
// Finalizers need to survive.
v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
}
}
}
void GlobalHandles::IterateYoungWeakObjectsForPhantomHandles(
RootVisitor* v, WeakSlotCallbackWithHeap should_reset_handle) {
for (Node* node : young_nodes_) {
......@@ -1392,42 +1306,6 @@ void GlobalHandles::InvokeSecondPassPhantomCallbacks() {
running_second_pass_callbacks_ = false;
}
size_t GlobalHandles::PostScavengeProcessing(unsigned post_processing_count) {
size_t freed_nodes = 0;
for (Node* node : young_nodes_) {
// Filter free nodes.
if (!node->IsRetainer()) continue;
if (node->IsPending()) {
DCHECK(node->has_callback());
DCHECK(node->IsPendingFinalizer());
node->PostGarbageCollectionProcessing(isolate_);
}
if (InRecursiveGC(post_processing_count)) return freed_nodes;
if (!node->IsRetainer()) freed_nodes++;
}
return freed_nodes;
}
size_t GlobalHandles::PostMarkSweepProcessing(unsigned post_processing_count) {
size_t freed_nodes = 0;
for (Node* node : *regular_nodes_) {
// Filter free nodes.
if (!node->IsRetainer()) continue;
if (node->IsPending()) {
DCHECK(node->has_callback());
DCHECK(node->IsPendingFinalizer());
node->PostGarbageCollectionProcessing(isolate_);
}
if (InRecursiveGC(post_processing_count)) return freed_nodes;
if (!node->IsRetainer()) freed_nodes++;
}
return freed_nodes;
}
template <typename T>
void GlobalHandles::UpdateAndCompactListOfYoungNode(
std::vector<T*>* node_list) {
......@@ -1527,29 +1405,22 @@ bool GlobalHandles::InRecursiveGC(unsigned gc_processing_counter) {
return gc_processing_counter != post_gc_processing_count_;
}
size_t GlobalHandles::PostGarbageCollectionProcessing(
void GlobalHandles::PostGarbageCollectionProcessing(
GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) {
// Process weak global handle callbacks. This must be done after the
// GC is completely done, because the callbacks may invoke arbitrary
// API functions.
DCHECK_EQ(Heap::NOT_IN_GC, isolate_->heap()->gc_state());
const unsigned post_processing_count = ++post_gc_processing_count_;
size_t freed_nodes = 0;
bool synchronous_second_pass =
isolate_->heap()->IsTearingDown() ||
(gc_callback_flags &
(kGCCallbackFlagForced | kGCCallbackFlagCollectAllAvailableGarbage |
kGCCallbackFlagSynchronousPhantomCallbackProcessing)) != 0;
InvokeOrScheduleSecondPassPhantomCallbacks(synchronous_second_pass);
if (InRecursiveGC(post_processing_count)) return freed_nodes;
freed_nodes += Heap::IsYoungGenerationCollector(collector)
? PostScavengeProcessing(post_processing_count)
: PostMarkSweepProcessing(post_processing_count);
if (InRecursiveGC(post_processing_count)) return freed_nodes;
if (InRecursiveGC(post_processing_count)) return;
UpdateListOfYoungNodes();
return freed_nodes;
}
void GlobalHandles::IterateStrongRoots(RootVisitor* v) {
......
......@@ -26,21 +26,6 @@ namespace internal {
class HeapStats;
class RootVisitor;
enum WeaknessType {
// Embedder gets a handle to the dying object.
FINALIZER_WEAK,
// In the following cases, the embedder gets the parameter they passed in
// earlier, and 0 or 2 first embedder fields. Note that the internal
// fields must contain aligned non-V8 pointers. Getting pointers to V8
// objects through this interface would be GC unsafe so in that case the
// embedder gets a null pointer instead.
PHANTOM_WEAK,
PHANTOM_WEAK_2_EMBEDDER_FIELDS,
// The handle is automatically reset by the garbage collector when
// the object is no longer reachable.
PHANTOM_WEAK_RESET_HANDLE
};
// Global handles hold handles that are independent of stack-state and can have
// callbacks and finalizers attached to them.
class V8_EXPORT_PRIVATE GlobalHandles final {
......@@ -118,9 +103,8 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
size_t InvokeFirstPassWeakCallbacks();
void InvokeSecondPassPhantomCallbacks();
// Process pending weak handles.
// Returns the number of freed nodes.
size_t PostGarbageCollectionProcessing(
// Schedule or invoke second pass weak callbacks.
void PostGarbageCollectionProcessing(
GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags);
void IterateStrongRoots(RootVisitor* v);
......@@ -144,13 +128,6 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
void IterateTracedNodes(
v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor);
// Marks handles with finalizers on the predicate |should_reset_handle| as
// pending.
void IterateWeakRootsIdentifyFinalizers(
WeakSlotCallbackWithHeap should_reset_handle);
// Uses the provided visitor |v| to mark handles with finalizers that are
// pending.
void IterateWeakRootsForFinalizers(RootVisitor* v);
// Marks handles that are phantom or have callbacks based on the predicate
// |should_reset_handle| as pending.
void IterateWeakRootsForPhantomHandles(
......@@ -164,12 +141,8 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
// Iterates over strong and dependent handles. See the note above.
void IterateYoungStrongAndDependentRoots(RootVisitor* v);
// Marks weak unmodified handles satisfying |is_dead| as pending.
void MarkYoungWeakDeadObjectsPending(WeakSlotCallbackWithHeap is_dead);
// Iterates over weak independent or unmodified handles.
// See the note above.
void IterateYoungWeakDeadObjectsForFinalizers(RootVisitor* v);
void IterateYoungWeakObjectsForPhantomHandles(
RootVisitor* v, WeakSlotCallbackWithHeap should_reset_handle);
......@@ -218,8 +191,6 @@ class V8_EXPORT_PRIVATE GlobalHandles final {
void InvokeSecondPassPhantomCallbacksFromTask();
void InvokeOrScheduleSecondPassPhantomCallbacks(bool synchronous_second_pass);
size_t PostScavengeProcessing(unsigned post_processing_count);
size_t PostMarkSweepProcessing(unsigned post_processing_count);
template <typename T>
size_t InvokeFirstPassWeakCallbacks(
......
......@@ -909,6 +909,7 @@ void GCTracer::PrintNVP() const {
"heap.external.weak_global_handles=%.1f "
"clear=%1.f "
"clear.external_string_table=%.1f "
"clear.weak_global_handles=%.1f "
"clear.dependent_code=%.1f "
"clear.maps=%.1f "
"clear.slots_buffer=%.1f "
......@@ -939,9 +940,6 @@ void GCTracer::PrintNVP() const {
"mark.weak_closure.ephemeron=%.1f "
"mark.weak_closure.ephemeron.marking=%.1f "
"mark.weak_closure.ephemeron.linear=%.1f "
"mark.weak_closure.weak_handles=%.1f "
"mark.weak_closure.weak_roots=%.1f "
"mark.weak_closure.harmony=%.1f "
"mark.embedder_prologue=%.1f "
"mark.embedder_tracing=%.1f "
"prologue=%.1f "
......@@ -1000,6 +998,7 @@ void GCTracer::PrintNVP() const {
current_scope(Scope::HEAP_EXTERNAL_WEAK_GLOBAL_HANDLES),
current_scope(Scope::MC_CLEAR),
current_scope(Scope::MC_CLEAR_EXTERNAL_STRING_TABLE),
current_scope(Scope::MC_CLEAR_WEAK_GLOBAL_HANDLES),
current_scope(Scope::MC_CLEAR_DEPENDENT_CODE),
current_scope(Scope::MC_CLEAR_MAPS),
current_scope(Scope::MC_CLEAR_SLOTS_BUFFER),
......@@ -1029,9 +1028,6 @@ void GCTracer::PrintNVP() const {
current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_WEAK_HANDLES),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_WEAK_ROOTS),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_HARMONY),
current_scope(Scope::MC_MARK_EMBEDDER_PROLOGUE),
current_scope(Scope::MC_MARK_EMBEDDER_TRACING),
current_scope(Scope::MC_PROLOGUE), current_scope(Scope::MC_SWEEP),
......
......@@ -1931,9 +1931,8 @@ bool Heap::CollectGarbage(AllocationSpace space,
{
AllowGarbageCollection allow_gc;
AllowJavascriptExecution allow_js(isolate());
freed_global_handles +=
isolate_->global_handles()->PostGarbageCollectionProcessing(
collector, gc_callback_flags);
isolate_->global_handles()->PostGarbageCollectionProcessing(
collector, gc_callback_flags);
}
gc_post_processing_depth_--;
}
......
......@@ -2440,47 +2440,6 @@ void MarkCompactCollector::MarkLiveObjects() {
ProcessEphemeronMarking();
DCHECK(local_marking_worklists()->IsEmpty());
}
// The objects reachable from the roots, weak maps, and embedder heap
// tracing are marked. Objects pointed to only by weak global handles cannot
// be immediately reclaimed. Instead, we have to mark them as pending and
// mark objects reachable from them.
//
// First we identify nonlive weak handles and mark them as pending
// destruction.
{
TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_HANDLES);
heap()->isolate()->global_handles()->IterateWeakRootsIdentifyFinalizers(
&IsUnmarkedHeapObject);
DrainMarkingWorklist();
}
// Process finalizers, effectively keeping them alive until the next
// garbage collection.
{
TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_ROOTS);
heap()->isolate()->global_handles()->IterateWeakRootsForFinalizers(
&root_visitor);
DrainMarkingWorklist();
}
// Repeat ephemeron processing from the newly marked objects.
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WEAK_CLOSURE_HARMONY);
ProcessEphemeronMarking();
DCHECK(local_marking_worklists()->IsWrapperEmpty());
DCHECK(local_marking_worklists()->IsEmpty());
}
// We depend on IterateWeakRootsForPhantomHandles being called before
// ProcessOldCodeCandidates in order to identify flushed bytecode in the
// CPU profiler.
{
heap()->isolate()->global_handles()->IterateWeakRootsForPhantomHandles(
&IsUnmarkedHeapObject);
}
}
if (was_marked_incrementally) {
......@@ -2577,6 +2536,15 @@ void MarkCompactCollector::ClearNonLiveReferences() {
heap()->external_string_table_.CleanUpAll();
}
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_WEAK_GLOBAL_HANDLES);
// We depend on `IterateWeakRootsForPhantomHandles()` being called before
// `ProcessOldCodeCandidates()` in order to identify flushed bytecode in the
// CPU profiler.
heap()->isolate()->global_handles()->IterateWeakRootsForPhantomHandles(
&IsUnmarkedHeapObject);
}
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_FLUSHABLE_BYTECODE);
// `ProcessFlusheBaselineCandidates()` must be called after
......@@ -5891,10 +5859,6 @@ void MinorMarkCompactCollector::MarkLiveObjects() {
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK_GLOBAL_HANDLES);
isolate()->global_handles()->MarkYoungWeakDeadObjectsPending(
&IsUnmarkedObjectForYoungGeneration);
isolate()->global_handles()->IterateYoungWeakDeadObjectsForFinalizers(
&root_visitor);
isolate()->global_handles()->IterateYoungWeakObjectsForPhantomHandles(
&root_visitor, &IsUnmarkedObjectForYoungGeneration);
DrainMarkingWorklist();
......
......@@ -366,16 +366,10 @@ void ScavengerCollector::CollectGarbage() {
// Scavenge weak global handles.
TRACE_GC(heap_->tracer(),
GCTracer::Scope::SCAVENGER_SCAVENGE_WEAK_GLOBAL_HANDLES_PROCESS);
isolate_->global_handles()->MarkYoungWeakDeadObjectsPending(
&IsUnscavengedHeapObjectSlot);
isolate_->global_handles()->IterateYoungWeakDeadObjectsForFinalizers(
&root_scavenge_visitor);
scavengers[kMainThreadId]->Process();
DCHECK(copied_list.IsEmpty());
DCHECK(promotion_list.IsEmpty());
isolate_->global_handles()->IterateYoungWeakObjectsForPhantomHandles(
&root_scavenge_visitor, &IsUnscavengedHeapObjectSlot);
DCHECK(copied_list.IsEmpty());
DCHECK(promotion_list.IsEmpty());
}
{
......
......@@ -543,6 +543,7 @@
F(MC_CLEAR_SLOTS_BUFFER) \
F(MC_CLEAR_STRING_TABLE) \
F(MC_CLEAR_WEAK_COLLECTIONS) \
F(MC_CLEAR_WEAK_GLOBAL_HANDLES) \
F(MC_CLEAR_WEAK_LISTS) \
F(MC_CLEAR_WEAK_REFERENCES) \
F(MC_SWEEP_EXTERNAL_POINTER_TABLE) \
......@@ -573,9 +574,6 @@
F(MC_MARK_WEAK_CLOSURE_EPHEMERON) \
F(MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING) \
F(MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR) \
F(MC_MARK_WEAK_CLOSURE_WEAK_HANDLES) \
F(MC_MARK_WEAK_CLOSURE_WEAK_ROOTS) \
F(MC_MARK_WEAK_CLOSURE_HARMONY) \
F(MC_SWEEP_CODE) \
F(MC_SWEEP_MAP) \
F(MC_SWEEP_OLD) \
......
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