Commit 2b77aaf4 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[global-handles] Restructure GC post processing

Bug: chromium:923361
Change-Id: I97a0314b6d5af543e0574f27892c73637739844d
Reviewed-on: https://chromium-review.googlesource.com/c/1426124
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58984}
parent 7f12e75a
...@@ -373,6 +373,12 @@ class GlobalHandles::Node final { ...@@ -373,6 +373,12 @@ class GlobalHandles::Node final {
return state() == PENDING && IsPhantomResetHandle(); return state() == PENDING && IsPhantomResetHandle();
} }
bool IsPendingFinalizer() const {
return state() == PENDING && weakness_type() == FINALIZER_WEAK;
}
bool IsPending() const { return state() == PENDING; }
bool IsRetainer() const { bool IsRetainer() const {
return state() != FREE && return state() != FREE &&
!(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK); !(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK);
...@@ -400,6 +406,8 @@ class GlobalHandles::Node final { ...@@ -400,6 +406,8 @@ class GlobalHandles::Node final {
return data_.parameter; return data_.parameter;
} }
bool has_callback() const { return weak_callback_ != nullptr; }
// Accessors for next free node in the free list. // Accessors for next free node in the free list.
Node* next_free() { Node* next_free() {
DCHECK_EQ(FREE, state()); DCHECK_EQ(FREE, state());
...@@ -490,25 +498,16 @@ class GlobalHandles::Node final { ...@@ -490,25 +498,16 @@ class GlobalHandles::Node final {
NodeSpace<Node>::Release(this); NodeSpace<Node>::Release(this);
} }
bool PostGarbageCollectionProcessing(Isolate* isolate) { void InvokeFinalizer(Isolate* isolate) {
// Handles only weak handles (not phantom) that are dying. CHECK(IsPendingFinalizer());
if (state() != Node::PENDING) return false; CHECK(!is_active());
if (weak_callback_ == nullptr) {
NodeSpace<Node>::Release(this);
return false;
}
set_state(NEAR_DEATH); set_state(NEAR_DEATH);
// Check that we are not passing a finalized external string to // Check that we are not passing a finalized external string to
// the callback. // the callback.
DCHECK(!object()->IsExternalOneByteString() || DCHECK(!object()->IsExternalOneByteString() ||
ExternalOneByteString::cast(object())->resource() != nullptr); ExternalOneByteString::cast(object())->resource() != nullptr);
DCHECK(!object()->IsExternalTwoByteString() || DCHECK(!object()->IsExternalTwoByteString() ||
ExternalTwoByteString::cast(object())->resource() != nullptr); ExternalTwoByteString::cast(object())->resource() != nullptr);
if (weakness_type() != FINALIZER_WEAK) {
return false;
}
// Leaving V8. // Leaving V8.
VMState<EXTERNAL> vmstate(isolate); VMState<EXTERNAL> vmstate(isolate);
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
...@@ -517,11 +516,9 @@ class GlobalHandles::Node final { ...@@ -517,11 +516,9 @@ class GlobalHandles::Node final {
v8::WeakCallbackInfo<void> data(reinterpret_cast<v8::Isolate*>(isolate), v8::WeakCallbackInfo<void> data(reinterpret_cast<v8::Isolate*>(isolate),
parameter(), embedder_fields, nullptr); parameter(), embedder_fields, nullptr);
weak_callback_(data); weak_callback_(data);
// For finalizers the handle must have either been reset or made strong.
// Absence of explicit cleanup or revival of weak handle // Both cases reset the state.
// in most of the cases would lead to memory leak. CHECK_NE(NEAR_DEATH, state());
CHECK(state() != NEAR_DEATH);
return true;
} }
inline GlobalHandles* GetGlobalHandles(); inline GlobalHandles* GetGlobalHandles();
...@@ -819,61 +816,44 @@ void GlobalHandles::InvokeSecondPassPhantomCallbacks() { ...@@ -819,61 +816,44 @@ void GlobalHandles::InvokeSecondPassPhantomCallbacks() {
} }
} }
int GlobalHandles::PostScavengeProcessing( size_t GlobalHandles::PostScavengeProcessing(unsigned post_processing_count) {
unsigned initial_post_gc_processing_count) { size_t freed_nodes = 0;
int freed_nodes = 0;
for (Node* node : new_space_nodes_) { for (Node* node : new_space_nodes_) {
DCHECK(node->is_in_new_space_list()); // Filter free nodes.
if (!node->IsRetainer()) { if (!node->IsRetainer()) continue;
// Free nodes do not have weak callbacks. Do not use them to compute
// the freed_nodes. // Reset active state for all affected nodes.
continue;
}
// Skip dependent or unmodified handles. Their weak callbacks might expect
// to be
// called between two global garbage collection callbacks which
// are not called for minor collections.
if (!node->is_independent() && (node->is_active())) {
node->set_active(false);
continue;
}
node->set_active(false); node->set_active(false);
if (node->PostGarbageCollectionProcessing(isolate_)) { if (node->IsPending()) {
if (initial_post_gc_processing_count != post_gc_processing_count_) { DCHECK(node->has_callback());
// Weak callback triggered another GC and another round of DCHECK(node->IsPendingFinalizer());
// PostGarbageCollection processing. The current node might node->InvokeFinalizer(isolate_);
// have been deleted in that round, so we need to bail out (or
// restart the processing).
return freed_nodes;
}
}
if (!node->IsRetainer()) {
freed_nodes++;
} }
if (InRecursiveGC(post_processing_count)) return freed_nodes;
if (!node->IsRetainer()) freed_nodes++;
} }
return freed_nodes; return freed_nodes;
} }
int GlobalHandles::PostMarkSweepProcessing( size_t GlobalHandles::PostMarkSweepProcessing(unsigned post_processing_count) {
unsigned initial_post_gc_processing_count) { size_t freed_nodes = 0;
int freed_nodes = 0;
for (Node* node : *regular_nodes_) { for (Node* node : *regular_nodes_) {
if (!node->IsRetainer()) { // Filter free nodes.
// Free nodes do not have weak callbacks. Do not use them to compute if (!node->IsRetainer()) continue;
// the freed_nodes.
continue; // Reset active state for all affected nodes.
}
node->set_active(false); node->set_active(false);
if (node->PostGarbageCollectionProcessing(isolate_)) {
if (initial_post_gc_processing_count != post_gc_processing_count_) { if (node->IsPending()) {
// See the comment above. DCHECK(node->has_callback());
return freed_nodes; DCHECK(node->IsPendingFinalizer());
} node->InvokeFinalizer(isolate_);
}
if (!node->IsRetainer()) {
freed_nodes++;
} }
if (InRecursiveGC(post_processing_count)) return freed_nodes;
if (!node->IsRetainer()) freed_nodes++;
} }
return freed_nodes; return freed_nodes;
} }
...@@ -900,8 +880,8 @@ void GlobalHandles::UpdateListOfNewSpaceNodes() { ...@@ -900,8 +880,8 @@ void GlobalHandles::UpdateListOfNewSpaceNodes() {
new_space_nodes_.shrink_to_fit(); new_space_nodes_.shrink_to_fit();
} }
int GlobalHandles::InvokeFirstPassWeakCallbacks() { size_t GlobalHandles::InvokeFirstPassWeakCallbacks() {
int freed_nodes = 0; size_t freed_nodes = 0;
std::vector<PendingPhantomCallback> pending_phantom_callbacks; std::vector<PendingPhantomCallback> pending_phantom_callbacks;
pending_phantom_callbacks.swap(pending_phantom_callbacks_); pending_phantom_callbacks.swap(pending_phantom_callbacks_);
{ {
...@@ -959,38 +939,32 @@ void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) { ...@@ -959,38 +939,32 @@ void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) {
} }
} }
int GlobalHandles::PostGarbageCollectionProcessing( bool GlobalHandles::InRecursiveGC(unsigned gc_processing_counter) {
return gc_processing_counter != post_gc_processing_count_;
}
size_t GlobalHandles::PostGarbageCollectionProcessing(
GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) { GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) {
// Process weak global handle callbacks. This must be done after the // Process weak global handle callbacks. This must be done after the
// GC is completely done, because the callbacks may invoke arbitrary // GC is completely done, because the callbacks may invoke arbitrary
// API functions. // API functions.
DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC); DCHECK_EQ(Heap::NOT_IN_GC, isolate_->heap()->gc_state());
const unsigned initial_post_gc_processing_count = ++post_gc_processing_count_; const unsigned post_processing_count = ++post_gc_processing_count_;
int freed_nodes = 0; size_t freed_nodes = 0;
bool synchronous_second_pass = bool synchronous_second_pass =
isolate_->heap()->IsTearingDown() || isolate_->heap()->IsTearingDown() ||
(gc_callback_flags & (gc_callback_flags &
(kGCCallbackFlagForced | kGCCallbackFlagCollectAllAvailableGarbage | (kGCCallbackFlagForced | kGCCallbackFlagCollectAllAvailableGarbage |
kGCCallbackFlagSynchronousPhantomCallbackProcessing)) != 0; kGCCallbackFlagSynchronousPhantomCallbackProcessing)) != 0;
InvokeOrScheduleSecondPassPhantomCallbacks(synchronous_second_pass); InvokeOrScheduleSecondPassPhantomCallbacks(synchronous_second_pass);
if (initial_post_gc_processing_count != post_gc_processing_count_) { if (InRecursiveGC(post_processing_count)) return freed_nodes;
// If the callbacks caused a nested GC, then return. See comment in
// PostScavengeProcessing. freed_nodes += Heap::IsYoungGenerationCollector(collector)
return freed_nodes; ? PostScavengeProcessing(post_processing_count)
} : PostMarkSweepProcessing(post_processing_count);
if (Heap::IsYoungGenerationCollector(collector)) { if (InRecursiveGC(post_processing_count)) return freed_nodes;
freed_nodes += PostScavengeProcessing(initial_post_gc_processing_count);
} else { UpdateListOfNewSpaceNodes();
freed_nodes += PostMarkSweepProcessing(initial_post_gc_processing_count);
}
if (initial_post_gc_processing_count != post_gc_processing_count_) {
// If the callbacks caused a nested GC, then return. See comment in
// PostScavengeProcessing.
return freed_nodes;
}
if (initial_post_gc_processing_count == post_gc_processing_count_) {
UpdateListOfNewSpaceNodes();
}
return freed_nodes; return freed_nodes;
} }
......
...@@ -89,12 +89,12 @@ class GlobalHandles final { ...@@ -89,12 +89,12 @@ class GlobalHandles final {
void RecordStats(HeapStats* stats); void RecordStats(HeapStats* stats);
int InvokeFirstPassWeakCallbacks(); size_t InvokeFirstPassWeakCallbacks();
void InvokeSecondPassPhantomCallbacks(); void InvokeSecondPassPhantomCallbacks();
// Process pending weak handles. // Process pending weak handles.
// Returns the number of freed nodes. // Returns the number of freed nodes.
int PostGarbageCollectionProcessing( size_t PostGarbageCollectionProcessing(
GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags); GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags);
void IterateStrongRoots(RootVisitor* v); void IterateStrongRoots(RootVisitor* v);
...@@ -179,11 +179,15 @@ class GlobalHandles final { ...@@ -179,11 +179,15 @@ class GlobalHandles final {
class NodeSpace; class NodeSpace;
class PendingPhantomCallback; class PendingPhantomCallback;
bool InRecursiveGC(unsigned gc_processing_counter);
void InvokeSecondPassPhantomCallbacksFromTask(); void InvokeSecondPassPhantomCallbacksFromTask();
int PostScavengeProcessing(unsigned initial_post_gc_processing_count);
int PostMarkSweepProcessing(unsigned initial_post_gc_processing_count);
void InvokeOrScheduleSecondPassPhantomCallbacks(bool synchronous_second_pass); void InvokeOrScheduleSecondPassPhantomCallbacks(bool synchronous_second_pass);
size_t PostScavengeProcessing(unsigned post_processing_count);
size_t PostMarkSweepProcessing(unsigned post_processing_count);
void UpdateListOfNewSpaceNodes(); void UpdateListOfNewSpaceNodes();
void ApplyPersistentHandleVisitor(v8::PersistentHandleVisitor* visitor, void ApplyPersistentHandleVisitor(v8::PersistentHandleVisitor* visitor,
Node* node); Node* node);
......
...@@ -1644,7 +1644,7 @@ void Heap::UpdateSurvivalStatistics(int start_new_space_size) { ...@@ -1644,7 +1644,7 @@ void Heap::UpdateSurvivalStatistics(int start_new_space_size) {
bool Heap::PerformGarbageCollection( bool Heap::PerformGarbageCollection(
GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) { GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) {
int freed_global_handles = 0; size_t freed_global_handles = 0;
if (!IsYoungGenerationCollector(collector)) { if (!IsYoungGenerationCollector(collector)) {
PROFILE(isolate_, CodeMovingGCEvent()); PROFILE(isolate_, CodeMovingGCEvent());
......
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