cppgc: Add getters internal heap state

Adds getters for GC phases to be used by advanced embedders to ensure
and check consistency conditions as needed.

Bug: chromium:1056170
Change-Id: Ia0b219f838bf31f0edbfe40585b95bb5eafa734d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2658328
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72430}
This commit is contained in:
Michael Lippautz 2021-01-29 12:21:43 +01:00 committed by Commit Bot
parent a3be3e83c1
commit 732e22e088
10 changed files with 116 additions and 14 deletions

View File

@ -143,10 +143,31 @@ class V8_EXPORT HeapState final {
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently marking, and false
* otherwise.
* otherwise.
*/
static bool IsMarking(HeapHandle& heap_handle);
/*
* Returns whether the garbage collector is sweeping. This API is experimental
* and is expected to be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently sweeping, and false
* otherwise.
*/
static bool IsSweeping(HeapHandle& heap_handle);
/**
* Returns whether the garbage collector is in the atomic pause, i.e., the
* mutator is stopped from running. This API is experimental and is expected
* to be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently in the atomic pause,
* and false otherwise.
*/
static bool IsInAtomicPause(HeapHandle& heap_handle);
private:
HeapState() = delete;
};
@ -177,7 +198,7 @@ class V8_EXPORT V8_NODISCARD DisallowGarbageCollectionScope final {
static void Enter(HeapHandle& heap_handle);
/**
* LEaves a disallow garbage collection scope. Must be paired with `Enter()`.
* Leaves a disallow garbage collection scope. Must be paired with `Enter()`.
* Prefer a scope instance of `DisallowGarbageCollectionScope`.
*
* \param heap_handle The corresponding heap.

View File

@ -241,17 +241,16 @@ bool CppHeap::AdvanceTracing(double deadline_in_ms) {
// accounting since this scope is also accounted under an outer v8 scope.
// Make sure to only account this scope once.
cppgc::internal::StatsCollector::EnabledScope stats_scope(
AsBase(), is_in_final_pause_
AsBase(), in_atomic_pause_
? cppgc::internal::StatsCollector::kAtomicMark
: cppgc::internal::StatsCollector::kIncrementalMark);
v8::base::TimeDelta deadline =
is_in_final_pause_
? v8::base::TimeDelta::Max()
: v8::base::TimeDelta::FromMillisecondsD(deadline_in_ms);
in_atomic_pause_ ? v8::base::TimeDelta::Max()
: v8::base::TimeDelta::FromMillisecondsD(deadline_in_ms);
// TODO(chromium:1056170): Replace when unified heap transitions to
// bytes-based deadline.
marking_done_ = marker_->AdvanceMarkingWithMaxDuration(deadline);
DCHECK_IMPLIES(is_in_final_pause_, marking_done_);
DCHECK_IMPLIES(in_atomic_pause_, marking_done_);
return marking_done_;
}
@ -261,7 +260,7 @@ void CppHeap::EnterFinalPause(EmbedderStackState stack_state) {
CHECK(!in_disallow_gc_scope());
cppgc::internal::StatsCollector::EnabledScope stats_scope(
AsBase(), cppgc::internal::StatsCollector::kAtomicMark);
is_in_final_pause_ = true;
in_atomic_pause_ = true;
marker_->EnterAtomicPause(stack_state);
if (compactor_.CancelIfShouldNotCompact(cppgc::Heap::MarkingType::kAtomic,
stack_state)) {
@ -270,14 +269,13 @@ void CppHeap::EnterFinalPause(EmbedderStackState stack_state) {
}
void CppHeap::TraceEpilogue(TraceSummary* trace_summary) {
CHECK(is_in_final_pause_);
CHECK(in_atomic_pause_);
CHECK(marking_done_);
{
cppgc::internal::StatsCollector::EnabledScope stats_scope(
AsBase(), cppgc::internal::StatsCollector::kAtomicMark);
cppgc::subtle::DisallowGarbageCollectionScope disallow_gc_scope(*this);
marker_->LeaveAtomicPause();
is_in_final_pause_ = false;
}
{
cppgc::subtle::DisallowGarbageCollectionScope disallow_gc_scope(*this);
@ -300,6 +298,7 @@ void CppHeap::TraceEpilogue(TraceSummary* trace_summary) {
compactable_space_handling};
sweeper().Start(sweeping_config);
}
in_atomic_pause_ = false;
sweeper().NotifyDoneIfNeeded();
}

View File

@ -71,7 +71,6 @@ class V8_EXPORT_PRIVATE CppHeap final
Isolate& isolate_;
bool marking_done_ = false;
bool is_in_final_pause_ = false;
// Buffered allocated bytes. Reporting allocated bytes to V8 can trigger a GC
// atomic pause. Allocated bytes are buffer in case this is temporarily

View File

@ -42,6 +42,13 @@ class GarbageCollector {
MarkingType::kIncremental, SweepingType::kAtomic};
}
static constexpr Config
PreciseIncrementalMarkingConcurrentSweepingConfig() {
return {CollectionType::kMajor, StackState::kNoHeapPointers,
MarkingType::kIncremental,
SweepingType::kIncrementalAndConcurrent};
}
static constexpr Config MinorPreciseAtomicConfig() {
return {CollectionType::kMinor, StackState::kNoHeapPointers,
MarkingType::kAtomic, SweepingType::kAtomic};

View File

@ -108,6 +108,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
const ObjectAllocator& object_allocator() const { return object_allocator_; }
Sweeper& sweeper() { return sweeper_; }
const Sweeper& sweeper() const { return sweeper_; }
PersistentRegion& GetStrongPersistentRegion() {
return strong_persistent_region_;
@ -150,6 +151,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
void Terminate();
bool in_disallow_gc_scope() const { return disallow_gc_scope_ > 0; }
bool in_atomic_pause() const { return in_atomic_pause_; }
protected:
virtual void FinalizeIncrementalGarbageCollectionIfNeeded(
@ -189,6 +191,8 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
const StackSupport stack_support_;
bool in_atomic_pause_ = false;
friend class MarkerBase::IncrementalMarkingTask;
friend class testing::TestWithHeap;
friend class cppgc::subtle::DisallowGarbageCollectionScope;

View File

@ -68,5 +68,17 @@ bool HeapState::IsMarking(HeapHandle& heap_handle) {
return heap_base.marker();
}
// static
bool HeapState::IsSweeping(HeapHandle& heap_handle) {
const auto& heap_base = internal::HeapBase::From(heap_handle);
return heap_base.sweeper().IsSweepingInProgress();
}
// static
bool HeapState::IsInAtomicPause(HeapHandle& heap_handle) {
const auto& heap_base = internal::HeapBase::From(heap_handle);
return heap_base.in_atomic_pause();
}
} // namespace subtle
} // namespace cppgc

View File

@ -173,6 +173,7 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
DCHECK(!in_no_gc_scope());
CHECK(!in_disallow_gc_scope());
config_.stack_state = stack_state;
in_atomic_pause_ = true;
{
// This guards atomic pause marking, meaning that no internal method or
// external callbacks are allowed to allocate new objects.
@ -196,6 +197,7 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
config_.sweeping_type,
Sweeper::SweepingConfig::CompactableSpaceHandling::kSweep};
sweeper_.Start(sweeping_config);
in_atomic_pause_ = false;
sweeper_.NotifyDoneIfNeeded();
}

View File

@ -654,6 +654,8 @@ class Sweeper::SweeperImpl final {
return is_sweeping_on_mutator_thread_;
}
bool IsSweepingInProgress() const { return is_in_progress_; }
private:
class MutatorThreadSweepingScope final {
public:
@ -791,5 +793,9 @@ bool Sweeper::IsSweepingOnMutatorThread() const {
return impl_->IsSweepingOnMutatorThread();
}
bool Sweeper::IsSweepingInProgress() const {
return impl_->IsSweepingInProgress();
}
} // namespace internal
} // namespace cppgc

View File

@ -48,6 +48,7 @@ class V8_EXPORT_PRIVATE Sweeper final {
bool SweepForAllocationIfRunning(NormalPageSpace* space, size_t size);
bool IsSweepingOnMutatorThread() const;
bool IsSweepingInProgress() const;
private:
void WaitForConcurrentSweepingForTesting();

View File

@ -11,6 +11,7 @@
#include "include/cppgc/allocation.h"
#include "include/cppgc/heap-consistency.h"
#include "include/cppgc/persistent.h"
#include "include/cppgc/prefinalizer.h"
#include "src/heap/cppgc/globals.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -186,15 +187,65 @@ TEST_F(GCHeapTest, IsGarbageCollectionAllowed) {
}
}
TEST_F(GCHeapTest, IsIncrementalMarking) {
GarbageCollector::Config config =
GarbageCollector::Config::PreciseIncrementalConfig();
TEST_F(GCHeapTest, IsMarking) {
GarbageCollector::Config config = GarbageCollector::Config::
PreciseIncrementalMarkingConcurrentSweepingConfig();
auto* heap = Heap::From(GetHeap());
EXPECT_FALSE(subtle::HeapState::IsMarking(*heap));
heap->StartIncrementalGarbageCollection(config);
EXPECT_TRUE(subtle::HeapState::IsMarking(*heap));
heap->FinalizeIncrementalGarbageCollectionIfRunning(config);
EXPECT_FALSE(subtle::HeapState::IsMarking(*heap));
heap->AsBase().sweeper().FinishIfRunning();
EXPECT_FALSE(subtle::HeapState::IsMarking(*heap));
}
TEST_F(GCHeapTest, IsSweeping) {
GarbageCollector::Config config = GarbageCollector::Config::
PreciseIncrementalMarkingConcurrentSweepingConfig();
auto* heap = Heap::From(GetHeap());
EXPECT_FALSE(subtle::HeapState::IsSweeping(*heap));
heap->StartIncrementalGarbageCollection(config);
EXPECT_FALSE(subtle::HeapState::IsSweeping(*heap));
heap->FinalizeIncrementalGarbageCollectionIfRunning(config);
EXPECT_TRUE(subtle::HeapState::IsSweeping(*heap));
heap->AsBase().sweeper().FinishIfRunning();
EXPECT_FALSE(subtle::HeapState::IsSweeping(*heap));
}
namespace {
class ExpectAtomicPause final : public GarbageCollected<ExpectAtomicPause> {
CPPGC_USING_PRE_FINALIZER(ExpectAtomicPause, PreFinalizer);
public:
explicit ExpectAtomicPause(HeapHandle& handle) : handle_(handle) {}
~ExpectAtomicPause() {
EXPECT_TRUE(subtle::HeapState::IsInAtomicPause(handle_));
}
void PreFinalizer() {
EXPECT_TRUE(subtle::HeapState::IsInAtomicPause(handle_));
}
void Trace(Visitor*) const {}
private:
HeapHandle& handle_;
};
} // namespace
TEST_F(GCHeapTest, IsInAtomicPause) {
GarbageCollector::Config config =
GarbageCollector::Config::PreciseIncrementalConfig();
auto* heap = Heap::From(GetHeap());
MakeGarbageCollected<ExpectAtomicPause>(heap->object_allocator(), *heap);
EXPECT_FALSE(subtle::HeapState::IsInAtomicPause(*heap));
heap->StartIncrementalGarbageCollection(config);
EXPECT_FALSE(subtle::HeapState::IsInAtomicPause(*heap));
heap->FinalizeIncrementalGarbageCollectionIfRunning(config);
EXPECT_FALSE(subtle::HeapState::IsInAtomicPause(*heap));
heap->AsBase().sweeper().FinishIfRunning();
EXPECT_FALSE(subtle::HeapState::IsInAtomicPause(*heap));
}
TEST_F(GCHeapTest, TerminateEmptyHeap) { Heap::From(GetHeap())->Terminate(); }