Commit e072615c authored by Omer Katz's avatar Omer Katz Committed by Commit Bot

cppgc: Establish marking invariants

This is a revival of https://chromium-review.googlesource.com/c/v8/v8/+/2228332

The CL establishes the following:
*) Objects are marked before being pushed to the worklists.
*) Live bytes are always accounted after tracing an object (i.e. move
   from Gray to Black below).
*) Previously not fully constructed objects are traced immediately
   instead of pushed to the marking worklist.

This establishes the following invariants for all marking worklists:
1) White = !object.is_marked() && !worklist.contains(object)
2) Gray = object.is_marked() && worklist.contains(object)
3) Black = object.is_marked() && !worklist.contains(object)

Bug: chromium:1056170
Change-Id: I821573b3fbc057e6ffb836154271ff986ecb4d2b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2336797Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69268}
parent 47434265
...@@ -98,6 +98,15 @@ bool DrainWorklistWithDeadline(v8::base::TimeTicks deadline, Worklist* worklist, ...@@ -98,6 +98,15 @@ bool DrainWorklistWithDeadline(v8::base::TimeTicks deadline, Worklist* worklist,
return true; return true;
} }
void TraceMarkedObject(Visitor* visitor, const HeapObjectHeader* header) {
DCHECK(header);
DCHECK(!header->IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>());
DCHECK(header->IsMarked<HeapObjectHeader::AccessMode::kNonAtomic>());
const GCInfo& gcinfo =
GlobalGCInfoTable::GCInfoFromIndex(header->GetGCInfoIndex());
gcinfo.trace(visitor, header->Payload());
}
} // namespace } // namespace
MarkerBase::MarkerBase(HeapBase& heap) MarkerBase::MarkerBase(HeapBase& heap)
...@@ -115,15 +124,12 @@ MarkerBase::~MarkerBase() { ...@@ -115,15 +124,12 @@ MarkerBase::~MarkerBase() {
if (!marking_worklists_.not_fully_constructed_worklist()->IsEmpty()) { if (!marking_worklists_.not_fully_constructed_worklist()->IsEmpty()) {
#if DEBUG #if DEBUG
DCHECK_NE(MarkingConfig::StackState::kNoHeapPointers, config_.stack_state); DCHECK_NE(MarkingConfig::StackState::kNoHeapPointers, config_.stack_state);
MarkingWorklists::NotFullyConstructedItem item; HeapObjectHeader* header;
MarkingWorklists::NotFullyConstructedWorklist::View view( MarkingWorklists::NotFullyConstructedWorklist::View view(
marking_worklists_.not_fully_constructed_worklist(), marking_worklists_.not_fully_constructed_worklist(),
MarkingWorklists::kMutatorThreadId); MarkingWorklists::kMutatorThreadId);
while (view.Pop(&item)) { while (view.Pop(&header)) {
const HeapObjectHeader& header = DCHECK(header->IsMarked());
BasePage::FromPayload(item)->ObjectHeaderFromInnerAddress(
static_cast<ConstAddress>(item));
DCHECK(header.IsMarked());
} }
#else #else
marking_worklists_.not_fully_constructed_worklist()->Clear(); marking_worklists_.not_fully_constructed_worklist()->Clear();
...@@ -204,9 +210,9 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) { ...@@ -204,9 +210,9 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) {
if (!DrainWorklistWithDeadline( if (!DrainWorklistWithDeadline(
deadline, deadline,
marking_worklists_.previously_not_fully_constructed_worklist(), marking_worklists_.previously_not_fully_constructed_worklist(),
[this](MarkingWorklists::NotFullyConstructedItem& item) { [this](HeapObjectHeader* header) {
mutator_marking_state_.DynamicallyMarkAddress( TraceMarkedObject(&visitor(), header);
reinterpret_cast<ConstAddress>(item)); mutator_marking_state_.AccountMarkedBytes(*header);
}, },
MarkingWorklists::kMutatorThreadId)) MarkingWorklists::kMutatorThreadId))
return false; return false;
...@@ -218,6 +224,8 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) { ...@@ -218,6 +224,8 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) {
HeapObjectHeader::FromPayload(item.base_object_payload); HeapObjectHeader::FromPayload(item.base_object_payload);
DCHECK(!header.IsInConstruction< DCHECK(!header.IsInConstruction<
HeapObjectHeader::AccessMode::kNonAtomic>()); HeapObjectHeader::AccessMode::kNonAtomic>());
DCHECK(
header.IsMarked<HeapObjectHeader::AccessMode::kNonAtomic>());
item.callback(&visitor(), item.base_object_payload); item.callback(&visitor(), item.base_object_payload);
mutator_marking_state_.AccountMarkedBytes(header); mutator_marking_state_.AccountMarkedBytes(header);
}, },
...@@ -227,12 +235,7 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) { ...@@ -227,12 +235,7 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) {
if (!DrainWorklistWithDeadline( if (!DrainWorklistWithDeadline(
deadline, marking_worklists_.write_barrier_worklist(), deadline, marking_worklists_.write_barrier_worklist(),
[this](HeapObjectHeader* header) { [this](HeapObjectHeader* header) {
DCHECK(header); TraceMarkedObject(&visitor(), header);
DCHECK(!header->IsInConstruction<
HeapObjectHeader::AccessMode::kNonAtomic>());
const GCInfo& gcinfo =
GlobalGCInfoTable::GCInfoFromIndex(header->GetGCInfoIndex());
gcinfo.trace(&visitor(), header->Payload());
mutator_marking_state_.AccountMarkedBytes(*header); mutator_marking_state_.AccountMarkedBytes(*header);
}, },
MarkingWorklists::kMutatorThreadId)) MarkingWorklists::kMutatorThreadId))
...@@ -244,12 +247,16 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) { ...@@ -244,12 +247,16 @@ bool MarkerBase::AdvanceMarkingWithDeadline(v8::base::TimeDelta duration) {
} }
void MarkerBase::MarkNotFullyConstructedObjects() { void MarkerBase::MarkNotFullyConstructedObjects() {
MarkingWorklists::NotFullyConstructedItem item; HeapObjectHeader* header;
MarkingWorklists::NotFullyConstructedWorklist::View view( MarkingWorklists::NotFullyConstructedWorklist::View view(
marking_worklists_.not_fully_constructed_worklist(), marking_worklists_.not_fully_constructed_worklist(),
MarkingWorklists::kMutatorThreadId); MarkingWorklists::kMutatorThreadId);
while (view.Pop(&item)) { while (view.Pop(&header)) {
conservative_visitor().TraceConservativelyIfNeeded(item); DCHECK(header);
DCHECK(header->IsMarked<HeapObjectHeader::AccessMode::kNonAtomic>());
// TraceConservativelyIfNeeded will either push to a worklist
// or trace conservatively and call AccountMarkedBytes.
conservative_visitor().TraceConservativelyIfNeeded(*header);
} }
} }
......
...@@ -82,7 +82,7 @@ class V8_EXPORT_PRIVATE MarkerBase { ...@@ -82,7 +82,7 @@ class V8_EXPORT_PRIVATE MarkerBase {
void ProcessWeakness(); void ProcessWeakness();
inline void WriteBarrierForInConstructionObject(ConstAddress); inline void WriteBarrierForInConstructionObject(HeapObjectHeader&);
inline void WriteBarrierForObject(HeapObjectHeader&); inline void WriteBarrierForObject(HeapObjectHeader&);
HeapBase& heap() { return heap_; } HeapBase& heap() { return heap_; }
...@@ -128,12 +128,12 @@ class V8_EXPORT_PRIVATE Marker final : public MarkerBase { ...@@ -128,12 +128,12 @@ class V8_EXPORT_PRIVATE Marker final : public MarkerBase {
ConservativeMarkingVisitor conservative_marking_visitor_; ConservativeMarkingVisitor conservative_marking_visitor_;
}; };
void MarkerBase::WriteBarrierForInConstructionObject(ConstAddress payload) { void MarkerBase::WriteBarrierForInConstructionObject(HeapObjectHeader& header) {
MarkingWorklists::NotFullyConstructedWorklist::View MarkingWorklists::NotFullyConstructedWorklist::View
not_fully_constructed_worklist( not_fully_constructed_worklist(
marking_worklists_.not_fully_constructed_worklist(), marking_worklists_.not_fully_constructed_worklist(),
MarkingWorklists::kMutatorThreadId); MarkingWorklists::kMutatorThreadId);
not_fully_constructed_worklist.Push(payload); not_fully_constructed_worklist.Push(&header);
} }
void MarkerBase::WriteBarrierForObject(HeapObjectHeader& header) { void MarkerBase::WriteBarrierForObject(HeapObjectHeader& header) {
......
...@@ -81,9 +81,11 @@ void MarkingState::MarkAndPush(const void* object, TraceDescriptor desc) { ...@@ -81,9 +81,11 @@ void MarkingState::MarkAndPush(const void* object, TraceDescriptor desc) {
void MarkingState::MarkAndPush(HeapObjectHeader& header, TraceDescriptor desc) { void MarkingState::MarkAndPush(HeapObjectHeader& header, TraceDescriptor desc) {
DCHECK_NOT_NULL(desc.callback); DCHECK_NOT_NULL(desc.callback);
if (!MarkNoPush(header)) return;
if (header.IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) { if (header.IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) {
not_fully_constructed_worklist_.Push(header.Payload()); not_fully_constructed_worklist_.Push(&header);
} else if (MarkNoPush(header)) { } else {
marking_worklist_.Push(desc); marking_worklist_.Push(desc);
} }
} }
......
...@@ -21,7 +21,6 @@ class MarkingWorklists { ...@@ -21,7 +21,6 @@ class MarkingWorklists {
static constexpr int kMutatorThreadId = 0; static constexpr int kMutatorThreadId = 0;
using MarkingItem = cppgc::TraceDescriptor; using MarkingItem = cppgc::TraceDescriptor;
using NotFullyConstructedItem = const void*;
struct WeakCallbackItem { struct WeakCallbackItem {
cppgc::WeakCallback callback; cppgc::WeakCallback callback;
const void* parameter; const void* parameter;
...@@ -32,7 +31,7 @@ class MarkingWorklists { ...@@ -32,7 +31,7 @@ class MarkingWorklists {
using MarkingWorklist = using MarkingWorklist =
Worklist<MarkingItem, 512 /* local entries */, kNumMarkers>; Worklist<MarkingItem, 512 /* local entries */, kNumMarkers>;
using NotFullyConstructedWorklist = using NotFullyConstructedWorklist =
Worklist<NotFullyConstructedItem, 16 /* local entries */, kNumMarkers>; Worklist<HeapObjectHeader*, 16 /* local entries */, kNumMarkers>;
using WeakCallbackWorklist = using WeakCallbackWorklist =
Worklist<WeakCallbackItem, 64 /* local entries */, kNumMarkers>; Worklist<WeakCallbackItem, 64 /* local entries */, kNumMarkers>;
using WriteBarrierWorklist = using WriteBarrierWorklist =
......
...@@ -63,13 +63,18 @@ void ConservativeTracingVisitor::TraceConservativelyIfNeeded( ...@@ -63,13 +63,18 @@ void ConservativeTracingVisitor::TraceConservativelyIfNeeded(
if (!header) return; if (!header) return;
if (!header->IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) { TraceConservativelyIfNeeded(*header);
}
void ConservativeTracingVisitor::TraceConservativelyIfNeeded(
HeapObjectHeader& header) {
if (!header.IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) {
visitor_.Visit( visitor_.Visit(
header->Payload(), header.Payload(),
{header->Payload(), {header.Payload(),
GlobalGCInfoTable::GCInfoFromIndex(header->GetGCInfoIndex()).trace}); GlobalGCInfoTable::GCInfoFromIndex(header.GetGCInfoIndex()).trace});
} else { } else {
VisitConservatively(*header, TraceConservatively); VisitConservatively(header, TraceConservatively);
} }
} }
......
...@@ -54,6 +54,7 @@ class ConservativeTracingVisitor { ...@@ -54,6 +54,7 @@ class ConservativeTracingVisitor {
delete; delete;
void TraceConservativelyIfNeeded(const void*); void TraceConservativelyIfNeeded(const void*);
void TraceConservativelyIfNeeded(HeapObjectHeader&);
protected: protected:
using TraceConservativelyCallback = void(ConservativeTracingVisitor*, using TraceConservativelyCallback = void(ConservativeTracingVisitor*,
......
...@@ -37,10 +37,7 @@ void MarkValue(const BasePage* page, MarkerBase* marker, const void* value) { ...@@ -37,10 +37,7 @@ void MarkValue(const BasePage* page, MarkerBase* marker, const void* value) {
if (V8_UNLIKELY( if (V8_UNLIKELY(
header header
.IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>())) { .IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>())) {
// It is assumed that objects on not_fully_constructed_worklist_ are not marker->WriteBarrierForInConstructionObject(header);
// marked.
header.Unmark();
marker->WriteBarrierForInConstructionObject(header.Payload());
return; return;
} }
......
...@@ -224,7 +224,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedEmptyStack) { ...@@ -224,7 +224,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedEmptyStack) {
Member<GCedWithCallback> member(obj); Member<GCedWithCallback> member(obj);
marker.VisitorForTesting().Trace(member); marker.VisitorForTesting().Trace(member);
}); });
EXPECT_FALSE(HeapObjectHeader::FromPayload(object).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
marker.FinishMarking({MarkingConfig::CollectionType::kMajor, marker.FinishMarking({MarkingConfig::CollectionType::kMajor,
MarkingConfig::StackState::kMayContainHeapPointers}); MarkingConfig::StackState::kMayContainHeapPointers});
EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
...@@ -240,7 +240,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedNonEmptyStack) { ...@@ -240,7 +240,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedNonEmptyStack) {
GetAllocationHandle(), [&marker](GCedWithCallback* obj) { GetAllocationHandle(), [&marker](GCedWithCallback* obj) {
Member<GCedWithCallback> member(obj); Member<GCedWithCallback> member(obj);
marker.VisitorForTesting().Trace(member); marker.VisitorForTesting().Trace(member);
EXPECT_FALSE(HeapObjectHeader::FromPayload(obj).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
marker.FinishMarking(config); marker.FinishMarking(config);
EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
}); });
......
...@@ -205,7 +205,7 @@ class GCedWithMixinWithInConstructionCallback ...@@ -205,7 +205,7 @@ class GCedWithMixinWithInConstructionCallback
} // namespace } // namespace
TEST_F(MarkingVisitorTest, DontMarkMemberInConstruction) { TEST_F(MarkingVisitorTest, MarkMemberInConstruction) {
TestMarkingVisitor visitor(GetMarker()); TestMarkingVisitor visitor(GetMarker());
GCedWithInConstructionCallback* gced = GCedWithInConstructionCallback* gced =
MakeGarbageCollected<GCedWithInConstructionCallback>( MakeGarbageCollected<GCedWithInConstructionCallback>(
...@@ -214,10 +214,10 @@ TEST_F(MarkingVisitorTest, DontMarkMemberInConstruction) { ...@@ -214,10 +214,10 @@ TEST_F(MarkingVisitorTest, DontMarkMemberInConstruction) {
Member<GCedWithInConstructionCallback> object(obj); Member<GCedWithInConstructionCallback> object(obj);
visitor.Trace(object); visitor.Trace(object);
}); });
EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
} }
TEST_F(MarkingVisitorTest, DontMarkMemberMixinInConstruction) { TEST_F(MarkingVisitorTest, MarkMemberMixinInConstruction) {
TestMarkingVisitor visitor(GetMarker()); TestMarkingVisitor visitor(GetMarker());
GCedWithMixinWithInConstructionCallback* gced = GCedWithMixinWithInConstructionCallback* gced =
MakeGarbageCollected<GCedWithMixinWithInConstructionCallback>( MakeGarbageCollected<GCedWithMixinWithInConstructionCallback>(
...@@ -226,7 +226,7 @@ TEST_F(MarkingVisitorTest, DontMarkMemberMixinInConstruction) { ...@@ -226,7 +226,7 @@ TEST_F(MarkingVisitorTest, DontMarkMemberMixinInConstruction) {
Member<MixinWithInConstructionCallback> mixin(obj); Member<MixinWithInConstructionCallback> mixin(obj);
visitor.Trace(mixin); visitor.Trace(mixin);
}); });
EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
} }
TEST_F(MarkingVisitorTest, DontMarkWeakMemberInConstruction) { TEST_F(MarkingVisitorTest, DontMarkWeakMemberInConstruction) {
...@@ -253,7 +253,7 @@ TEST_F(MarkingVisitorTest, DontMarkWeakMemberMixinInConstruction) { ...@@ -253,7 +253,7 @@ TEST_F(MarkingVisitorTest, DontMarkWeakMemberMixinInConstruction) {
EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
} }
TEST_F(MarkingVisitorTest, DontMarkPersistentInConstruction) { TEST_F(MarkingVisitorTest, MarkPersistentInConstruction) {
TestMarkingVisitor visitor(GetMarker()); TestMarkingVisitor visitor(GetMarker());
GCedWithInConstructionCallback* gced = GCedWithInConstructionCallback* gced =
MakeGarbageCollected<GCedWithInConstructionCallback>( MakeGarbageCollected<GCedWithInConstructionCallback>(
...@@ -262,10 +262,10 @@ TEST_F(MarkingVisitorTest, DontMarkPersistentInConstruction) { ...@@ -262,10 +262,10 @@ TEST_F(MarkingVisitorTest, DontMarkPersistentInConstruction) {
Persistent<GCedWithInConstructionCallback> object(obj); Persistent<GCedWithInConstructionCallback> object(obj);
visitor.TraceRootForTesting(object, SourceLocation::Current()); visitor.TraceRootForTesting(object, SourceLocation::Current());
}); });
EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
} }
TEST_F(MarkingVisitorTest, DontMarkPersistentMixinInConstruction) { TEST_F(MarkingVisitorTest, MarkPersistentMixinInConstruction) {
TestMarkingVisitor visitor(GetMarker()); TestMarkingVisitor visitor(GetMarker());
GCedWithMixinWithInConstructionCallback* gced = GCedWithMixinWithInConstructionCallback* gced =
MakeGarbageCollected<GCedWithMixinWithInConstructionCallback>( MakeGarbageCollected<GCedWithMixinWithInConstructionCallback>(
...@@ -274,7 +274,7 @@ TEST_F(MarkingVisitorTest, DontMarkPersistentMixinInConstruction) { ...@@ -274,7 +274,7 @@ TEST_F(MarkingVisitorTest, DontMarkPersistentMixinInConstruction) {
Persistent<MixinWithInConstructionCallback> mixin(obj); Persistent<MixinWithInConstructionCallback> mixin(obj);
visitor.TraceRootForTesting(mixin, SourceLocation::Current()); visitor.TraceRootForTesting(mixin, SourceLocation::Current());
}); });
EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
} }
} // namespace internal } // namespace internal
......
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