blob: d5321c6b3a28289615b1efbe110cc9b6c60c5211 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <initializer_list>
#include <vector>
#include "platform/heap/CallbackStack.h"
#include "platform/heap/GarbageCollected.h"
#include "platform/heap/Heap.h"
#include "platform/heap/HeapAllocator.h"
#include "platform/heap/HeapTerminatedArray.h"
#include "platform/heap/HeapTerminatedArrayBuilder.h"
#include "platform/heap/IncrementalMarkingFlag.h"
#include "platform/heap/Member.h"
#include "platform/heap/ThreadState.h"
#include "platform/heap/TraceTraits.h"
#include "platform/heap/Visitor.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
namespace blink {
namespace incremental_marking_test {
class IncrementalMarkingScope {
public:
explicit IncrementalMarkingScope(ThreadState* thread_state)
: gc_forbidden_scope_(thread_state),
thread_state_(thread_state),
heap_(thread_state_->Heap()),
marking_stack_(heap_.MarkingStack()) {
EXPECT_TRUE(marking_stack_->IsEmpty());
marking_stack_->Commit();
heap_.EnableIncrementalMarkingBarrier();
thread_state->current_gc_data_.visitor =
Visitor::Create(thread_state, Visitor::kGlobalMarking);
}
~IncrementalMarkingScope() {
EXPECT_TRUE(marking_stack_->IsEmpty());
heap_.DisableIncrementalMarkingBarrier();
marking_stack_->Decommit();
}
CallbackStack* marking_stack() const { return marking_stack_; }
ThreadHeap& heap() const { return heap_; }
protected:
ThreadState::GCForbiddenScope gc_forbidden_scope_;
ThreadState* const thread_state_;
ThreadHeap& heap_;
CallbackStack* const marking_stack_;
};
// Expects that the write barrier fires for the objects passed to the
// constructor. This requires that the objects are added to the marking stack
// as well as headers being marked.
template <typename T>
class ExpectWriteBarrierFires : public IncrementalMarkingScope {
public:
ExpectWriteBarrierFires(ThreadState* thread_state,
std::initializer_list<T*> objects)
: IncrementalMarkingScope(thread_state), objects_(objects) {
EXPECT_TRUE(marking_stack_->IsEmpty());
for (T* object : objects_) {
headers_.push_back(HeapObjectHeader::FromPayload(object));
EXPECT_FALSE(headers_.back()->IsMarked());
}
EXPECT_FALSE(objects_.empty());
}
~ExpectWriteBarrierFires() {
EXPECT_FALSE(marking_stack_->IsEmpty());
// All headers of objects watched should be marked.
for (HeapObjectHeader* header : headers_) {
EXPECT_TRUE(header->IsMarked());
}
// All objects watched should be on the marking stack.
while (!marking_stack_->IsEmpty()) {
CallbackStack::Item* item = marking_stack_->Pop();
T* obj = reinterpret_cast<T*>(item->Object());
auto pos = std::find(objects_.begin(), objects_.end(), obj);
// The following check makes sure that there are no unexpected objects on
// the marking stack. If it fails then the write barrier fired for an
// unexpected object.
EXPECT_NE(objects_.end(), pos);
// Avoid crashing.
if (objects_.end() != pos)
objects_.erase(pos);
}
EXPECT_TRUE(objects_.empty());
}
private:
std::vector<T*> objects_;
std::vector<HeapObjectHeader*> headers_;
};
// Expects that no write barrier fires for the objects passed to the
// constructor. This requires that the marking stack stays empty and the marking
// state of the object stays the same across the lifetime of the scope.
template <typename T>
class ExpectNoWriteBarrierFires : public IncrementalMarkingScope {
public:
ExpectNoWriteBarrierFires(ThreadState* thread_state,
std::initializer_list<T*> objects)
: IncrementalMarkingScope(thread_state), objects_(objects) {
EXPECT_TRUE(marking_stack_->IsEmpty());
for (T* object : objects_) {
HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
headers_.push_back(header);
was_marked_.push_back(header->IsMarked());
}
}
~ExpectNoWriteBarrierFires() {
EXPECT_TRUE(marking_stack_->IsEmpty());
for (size_t i = 0; i < headers_.size(); i++) {
EXPECT_EQ(was_marked_[i], headers_[i]->IsMarked());
}
}
private:
std::vector<T*> objects_;
std::vector<HeapObjectHeader*> headers_;
std::vector<bool> was_marked_;
};
class Object : public GarbageCollected<Object> {
public:
static Object* Create() { return new Object(); }
static Object* Create(Object* next) { return new Object(next); }
void set_next(Object* next) { next_ = next; }
virtual void Trace(blink::Visitor* visitor) { visitor->Trace(next_); }
bool IsMarked() const {
return HeapObjectHeader::FromPayload(this)->IsMarked();
}
private:
Object() : next_(nullptr) {}
explicit Object(Object* next) : next_(next) {}
Member<Object> next_;
};
// =============================================================================
// Basic infrastructure support. ===============================================
// =============================================================================
TEST(IncrementalMarkingTest, EnableDisableBarrier) {
Object* object = Object::Create();
BasePage* page = PageFromObject(object);
ThreadHeap& heap = ThreadState::Current()->Heap();
EXPECT_FALSE(page->IsIncrementalMarking());
EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
heap.EnableIncrementalMarkingBarrier();
EXPECT_TRUE(page->IsIncrementalMarking());
EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
heap.DisableIncrementalMarkingBarrier();
EXPECT_FALSE(page->IsIncrementalMarking());
EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
}
TEST(IncrementalMarkingTest, StackFrameDepthDisabled) {
IncrementalMarkingScope scope(ThreadState::Current());
EXPECT_FALSE(scope.heap().GetStackFrameDepth().IsSafeToRecurse());
}
TEST(IncrementalMarkingTest, ManualWriteBarrierTriggersWhenMarkingIsOn) {
Object* object = Object::Create();
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
EXPECT_FALSE(object->IsMarked());
ThreadState::Current()->Heap().WriteBarrier(object);
EXPECT_TRUE(object->IsMarked());
}
}
TEST(IncrementalMarkingTest, ManualWriteBarrierBailoutWhenMarkingIsOff) {
Object* object = Object::Create();
ThreadHeap& heap = ThreadState::Current()->Heap();
CallbackStack* marking_stack = heap.MarkingStack();
EXPECT_TRUE(marking_stack->IsEmpty());
EXPECT_FALSE(object->IsMarked());
heap.WriteBarrier(object);
EXPECT_FALSE(object->IsMarked());
EXPECT_TRUE(marking_stack->IsEmpty());
}
// =============================================================================
// Member<T> support. ==========================================================
// =============================================================================
TEST(IncrementalMarkingTest, MemberSetUnmarkedObject) {
Object* parent = Object::Create();
Object* child = Object::Create();
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {child});
EXPECT_FALSE(child->IsMarked());
parent->set_next(child);
EXPECT_TRUE(child->IsMarked());
}
}
TEST(IncrementalMarkingTest, MemberSetMarkedObjectNoBarrier) {
Object* parent = Object::Create();
Object* child = Object::Create();
HeapObjectHeader::FromPayload(child)->Mark();
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {child});
parent->set_next(child);
}
}
TEST(IncrementalMarkingTest, MemberInitializingStoreNoBarrier) {
Object* object1 = Object::Create();
HeapObjectHeader* object1_header = HeapObjectHeader::FromPayload(object1);
{
IncrementalMarkingScope scope(ThreadState::Current());
EXPECT_FALSE(object1_header->IsMarked());
Object* object2 = Object::Create(object1);
HeapObjectHeader* object2_header = HeapObjectHeader::FromPayload(object2);
EXPECT_FALSE(object1_header->IsMarked());
EXPECT_FALSE(object2_header->IsMarked());
}
}
TEST(IncrementalMarkingTest, MemberReferenceAssignMember) {
Object* obj = Object::Create();
Member<Object> m1;
Member<Object>& m2 = m1;
Member<Object> m3(obj);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
m2 = m3;
}
}
TEST(IncrementalMarkingTest, MemberSetDeletedValueNoBarrier) {
Member<Object> m;
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
m = WTF::kHashTableDeletedValue;
}
}
TEST(IncrementalMarkingTest, MemberCopyDeletedValueNoBarrier) {
Member<Object> m1(WTF::kHashTableDeletedValue);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
Member<Object> m2(m1);
}
}
TEST(IncrementalMarkingTest, MemberHashTraitConstructDeletedValueNoBarrier) {
Member<Object> m1;
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
HashTraits<Member<Object>>::ConstructDeletedValue(m1, false);
}
}
TEST(IncrementalMarkingTest, MemberHashTraitIsDeletedValueNoBarrier) {
Member<Object> m1(Object::Create());
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
EXPECT_FALSE(HashTraits<Member<Object>>::IsDeletedValue(m1));
}
}
// =============================================================================
// Mixin support. ==============================================================
// =============================================================================
namespace {
class Mixin : public GarbageCollectedMixin {
public:
Mixin() : next_(nullptr) {}
virtual ~Mixin() {}
virtual void Trace(blink::Visitor* visitor) { visitor->Trace(next_); }
virtual void Bar() {}
protected:
Member<Object> next_;
};
class ClassWithVirtual {
protected:
virtual void Foo() {}
};
class Child : public GarbageCollected<Child>,
public ClassWithVirtual,
public Mixin {
USING_GARBAGE_COLLECTED_MIXIN(Child);
public:
static Child* Create() { return new Child(); }
virtual ~Child() {}
virtual void Trace(blink::Visitor* visitor) { Mixin::Trace(visitor); }
virtual void Foo() {}
virtual void Bar() {}
protected:
Child() : ClassWithVirtual(), Mixin() {}
};
class ParentWithMixinPointer : public GarbageCollected<ParentWithMixinPointer> {
public:
static ParentWithMixinPointer* Create() {
return new ParentWithMixinPointer();
}
void set_mixin(Mixin* mixin) { mixin_ = mixin; }
virtual void Trace(blink::Visitor* visitor) { visitor->Trace(mixin_); }
protected:
ParentWithMixinPointer() : mixin_(nullptr) {}
Member<Mixin> mixin_;
};
} // namespace
TEST(IncrementalMarkingTest, WriteBarrierOnUnmarkedMixinApplication) {
ParentWithMixinPointer* parent = ParentWithMixinPointer::Create();
Child* child = Child::Create();
Mixin* mixin = static_cast<Mixin*>(child);
EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
{
ExpectWriteBarrierFires<Child> scope(ThreadState::Current(), {child});
parent->set_mixin(mixin);
}
}
TEST(IncrementalMarkingTest, NoWriteBarrierOnMarkedMixinApplication) {
ParentWithMixinPointer* parent = ParentWithMixinPointer::Create();
Child* child = Child::Create();
HeapObjectHeader::FromPayload(child)->Mark();
Mixin* mixin = static_cast<Mixin*>(child);
EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
{
ExpectNoWriteBarrierFires<Child> scope(ThreadState::Current(), {child});
parent->set_mixin(mixin);
}
}
// =============================================================================
// HeapVector support. =========================================================
// =============================================================================
namespace {
// HeapVector allows for insertion of container objects that can be traced but
// are themselves non-garbage collected.
class NonGarbageCollectedContainer {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
NonGarbageCollectedContainer(Object* obj, int y) : obj_(obj), y_(y) {}
virtual ~NonGarbageCollectedContainer() {}
virtual void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); }
private:
Member<Object> obj_;
int y_;
};
class NonGarbageCollectedContainerRoot {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
NonGarbageCollectedContainerRoot(Object* obj1, Object* obj2, int y)
: next_(obj1, y), obj_(obj2) {}
virtual ~NonGarbageCollectedContainerRoot() {}
virtual void Trace(blink::Visitor* visitor) {
visitor->Trace(next_);
visitor->Trace(obj_);
}
private:
NonGarbageCollectedContainer next_;
Member<Object> obj_;
};
} // namespace
TEST(IncrementalMarkingTest, HeapVectorPushBackMember) {
Object* obj = Object::Create();
HeapVector<Member<Object>> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
vec.push_back(obj);
}
}
TEST(IncrementalMarkingTest, HeapVectorPushBackNonGCedContainer) {
Object* obj = Object::Create();
HeapVector<NonGarbageCollectedContainer> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
vec.push_back(NonGarbageCollectedContainer(obj, 1));
}
}
TEST(IncrementalMarkingTest, HeapVectorPushBackStdPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<std::pair<Member<Object>, Member<Object>>> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
vec.push_back(std::make_pair(Member<Object>(obj1), Member<Object>(obj2)));
}
}
TEST(IncrementalMarkingTest, HeapVectorEmplaceBackMember) {
Object* obj = Object::Create();
HeapVector<Member<Object>> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
vec.emplace_back(obj);
}
}
TEST(IncrementalMarkingTest, HeapVectorEmplaceBackNonGCedContainer) {
Object* obj = Object::Create();
HeapVector<NonGarbageCollectedContainer> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
vec.emplace_back(obj, 1);
}
}
TEST(IncrementalMarkingTest, HeapVectorEmplaceBackStdPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<std::pair<Member<Object>, Member<Object>>> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
vec.emplace_back(obj1, obj2);
}
}
TEST(IncrementalMarkingTest, HeapVectorCopyMember) {
Object* object = Object::Create();
HeapVector<Member<Object>> vec1;
vec1.push_back(object);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
HeapVector<Member<Object>> vec2(vec1);
}
}
TEST(IncrementalMarkingTest, HeapVectorCopyNonGCedContainer) {
Object* obj = Object::Create();
HeapVector<NonGarbageCollectedContainer> vec1;
vec1.emplace_back(obj, 1);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
HeapVector<NonGarbageCollectedContainer> vec2(vec1);
}
}
TEST(IncrementalMarkingTest, HeapVectorCopyStdPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<std::pair<Member<Object>, Member<Object>>> vec1;
vec1.emplace_back(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
HeapVector<std::pair<Member<Object>, Member<Object>>> vec2(vec1);
}
}
TEST(IncrementalMarkingTest, HeapVectorMoveMember) {
Object* obj = Object::Create();
HeapVector<Member<Object>> vec1;
vec1.push_back(obj);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
HeapVector<Member<Object>> vec2(std::move(vec1));
}
}
TEST(IncrementalMarkingTest, HeapVectorMoveNonGCedContainer) {
Object* obj = Object::Create();
HeapVector<NonGarbageCollectedContainer> vec1;
vec1.emplace_back(obj, 1);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
HeapVector<NonGarbageCollectedContainer> vec2(std::move(vec1));
}
}
TEST(IncrementalMarkingTest, HeapVectorMoveStdPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<std::pair<Member<Object>, Member<Object>>> vec1;
vec1.emplace_back(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
HeapVector<std::pair<Member<Object>, Member<Object>>> vec2(std::move(vec1));
}
}
TEST(IncrementalMarkingTest, HeapVectorSwapMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<Member<Object>> vec1;
vec1.push_back(obj1);
HeapVector<Member<Object>> vec2;
vec2.push_back(obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
std::swap(vec1, vec2);
}
}
TEST(IncrementalMarkingTest, HeapVectorSwapNonGCedContainer) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<NonGarbageCollectedContainer> vec1;
vec1.emplace_back(obj1, 1);
HeapVector<NonGarbageCollectedContainer> vec2;
vec2.emplace_back(obj2, 2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
std::swap(vec1, vec2);
}
}
TEST(IncrementalMarkingTest, HeapVectorSwapStdPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<std::pair<Member<Object>, Member<Object>>> vec1;
vec1.emplace_back(obj1, nullptr);
HeapVector<std::pair<Member<Object>, Member<Object>>> vec2;
vec2.emplace_back(nullptr, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
std::swap(vec1, vec2);
}
}
TEST(IncrementalMarkingTest, HeapVectorSubscriptOperator) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapVector<Member<Object>> vec;
vec.push_back(obj1);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
EXPECT_EQ(1u, vec.size());
EXPECT_EQ(obj1, vec[0]);
vec[0] = obj2;
EXPECT_EQ(obj2, vec[0]);
EXPECT_FALSE(obj1->IsMarked());
}
}
TEST(IncrementalMarkingTest, HeapVectorEagerTracingStopsAtMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
obj1->set_next(obj3);
HeapVector<NonGarbageCollectedContainerRoot> vec;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
vec.emplace_back(obj1, obj2, 3);
// |obj3| is only reachable from |obj1| which is not eagerly traced. Only
// objects without object headers are eagerly traced.
EXPECT_FALSE(obj3->IsMarked());
}
}
// =============================================================================
// HeapDoublyLinkedList support. ===============================================
// =============================================================================
namespace {
class ObjectNode : public GarbageCollected<ObjectNode>,
public DoublyLinkedListNode<ObjectNode> {
public:
explicit ObjectNode(Object* obj) : obj_(obj) {}
void Trace(Visitor* visitor) {
visitor->Trace(obj_);
visitor->Trace(prev_);
visitor->Trace(next_);
}
private:
friend class WTF::DoublyLinkedListNode<ObjectNode>;
Member<Object> obj_;
Member<ObjectNode> prev_;
Member<ObjectNode> next_;
};
} // namespace
TEST(IncrementalMarkingTest, HeapDoublyLinkedListPush) {
Object* obj = Object::Create();
ObjectNode* obj_node = new ObjectNode(obj);
HeapDoublyLinkedList<ObjectNode> list;
{
ExpectWriteBarrierFires<ObjectNode> scope(ThreadState::Current(),
{obj_node});
list.Push(obj_node);
// |obj| will be marked once |obj_node| gets processed.
EXPECT_FALSE(obj->IsMarked());
}
}
TEST(IncrementalMarkingTest, HeapDoublyLinkedListAppend) {
Object* obj = Object::Create();
ObjectNode* obj_node = new ObjectNode(obj);
HeapDoublyLinkedList<ObjectNode> list;
{
ExpectWriteBarrierFires<ObjectNode> scope(ThreadState::Current(),
{obj_node});
list.Append(obj_node);
// |obj| will be marked once |obj_node| gets processed.
EXPECT_FALSE(obj->IsMarked());
}
}
// =============================================================================
// HeapDeque support. ==========================================================
// =============================================================================
TEST(IncrementalMarkingTest, HeapDequePushBackMember) {
Object* obj = Object::Create();
HeapDeque<Member<Object>> deq;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
deq.push_back(obj);
}
}
TEST(IncrementalMarkingTest, HeapDequePushFrontMember) {
Object* obj = Object::Create();
HeapDeque<Member<Object>> deq;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
deq.push_front(obj);
}
}
TEST(IncrementalMarkingTest, HeapDequeEmplaceBackMember) {
Object* obj = Object::Create();
HeapDeque<Member<Object>> deq;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
deq.emplace_back(obj);
}
}
TEST(IncrementalMarkingTest, HeapDequeEmplaceFrontMember) {
Object* obj = Object::Create();
HeapDeque<Member<Object>> deq;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
deq.emplace_front(obj);
}
}
TEST(IncrementalMarkingTest, HeapDequeCopyMember) {
Object* object = Object::Create();
HeapDeque<Member<Object>> deq1;
deq1.push_back(object);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
HeapDeque<Member<Object>> deq2(deq1);
}
}
TEST(IncrementalMarkingTest, HeapDequeMoveMember) {
Object* object = Object::Create();
HeapDeque<Member<Object>> deq1;
deq1.push_back(object);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
HeapDeque<Member<Object>> deq2(std::move(deq1));
}
}
TEST(IncrementalMarkingTest, HeapDequeSwapMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapDeque<Member<Object>> deq1;
deq1.push_back(obj1);
HeapDeque<Member<Object>> deq2;
deq2.push_back(obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
std::swap(deq1, deq2);
}
}
// =============================================================================
// HeapHashSet support. ========================================================
// =============================================================================
namespace {
template <typename Container>
void Insert() {
Object* obj = Object::Create();
Container container;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
container.insert(obj);
}
}
template <typename Container>
void InsertNoBarrier() {
Object* obj = Object::Create();
Container container;
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
container.insert(obj);
}
}
template <typename Container>
void Copy() {
Object* obj = Object::Create();
Container container1;
container1.insert(obj);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
Container container2(container1);
EXPECT_TRUE(container1.Contains(obj));
EXPECT_TRUE(container2.Contains(obj));
}
}
template <typename Container>
void CopyNoBarrier() {
Object* obj = Object::Create();
Container container1;
container1.insert(obj);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
Container container2(container1);
EXPECT_TRUE(container1.Contains(obj));
EXPECT_TRUE(container2.Contains(obj));
}
}
template <typename Container>
void Move() {
Object* obj = Object::Create();
Container container1;
container1.insert(obj);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
Container container2(std::move(container1));
}
}
template <typename Container>
void MoveNoBarrier() {
Object* obj = Object::Create();
Container container1;
container1.insert(obj);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
Container container2(std::move(container1));
}
}
template <typename Container>
void Swap() {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Container container1;
container1.insert(obj1);
Container container2;
container2.insert(obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
std::swap(container1, container2);
}
}
template <typename Container>
void SwapNoBarrier() {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Container container1;
container1.insert(obj1);
Container container2;
container2.insert(obj2);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2});
std::swap(container1, container2);
}
}
} // namespace
TEST(IncrementalMarkingTest, HeapHashSetInsert) {
Insert<HeapHashSet<Member<Object>>>();
InsertNoBarrier<HeapHashSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapHashSetCopy) {
Copy<HeapHashSet<Member<Object>>>();
CopyNoBarrier<HeapHashSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapHashSetMove) {
Move<HeapHashSet<Member<Object>>>();
MoveNoBarrier<HeapHashSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapHashSetSwap) {
Swap<HeapHashSet<Member<Object>>>();
SwapNoBarrier<HeapHashSet<WeakMember<Object>>>();
}
class StrongWeakPair : public std::pair<Member<Object>, WeakMember<Object>> {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
typedef std::pair<Member<Object>, WeakMember<Object>> Base;
public:
StrongWeakPair(Object* obj1, Object* obj2) : Base(obj1, obj2) {}
StrongWeakPair(WTF::HashTableDeletedValueType)
: Base(WTF::kHashTableDeletedValue, nullptr) {}
bool IsHashTableDeletedValue() const {
return first.IsHashTableDeletedValue();
}
// Trace will be called for write barrier invocations. Only strong members
// are interesting.
void Trace(blink::Visitor* visitor) { visitor->Trace(first); }
// TraceInCollection will be called for weak processing.
template <typename VisitorDispatcher>
bool TraceInCollection(VisitorDispatcher visitor,
WTF::ShouldWeakPointersBeMarkedStrongly strongify) {
visitor->Trace(first);
if (strongify == WTF::kWeakPointersActStrong) {
visitor->Trace(second);
}
return false;
}
};
} // namespace incremental_marking_test
} // namespace blink
namespace WTF {
template <>
struct HashTraits<blink::incremental_marking_test::StrongWeakPair>
: SimpleClassHashTraits<blink::incremental_marking_test::StrongWeakPair> {
static const WTF::WeakHandlingFlag kWeakHandlingFlag =
WTF::kWeakHandlingInCollections;
template <typename U = void>
struct IsTraceableInCollection {
static const bool value = true;
};
static const bool kHasIsEmptyValueFunction = true;
static bool IsEmptyValue(
const blink::incremental_marking_test::StrongWeakPair& value) {
return !value.first;
}
static void ConstructDeletedValue(
blink::incremental_marking_test::StrongWeakPair& slot,
bool) {
new (NotNull, &slot)
blink::incremental_marking_test::StrongWeakPair(kHashTableDeletedValue);
}
static bool IsDeletedValue(
const blink::incremental_marking_test::StrongWeakPair& value) {
return value.IsHashTableDeletedValue();
}
template <typename VisitorDispatcher>
static bool TraceInCollection(
VisitorDispatcher visitor,
blink::incremental_marking_test::StrongWeakPair& t,
WTF::ShouldWeakPointersBeMarkedStrongly strongify) {
return t.TraceInCollection(visitor, strongify);
}
};
template <>
struct DefaultHash<blink::incremental_marking_test::StrongWeakPair> {
typedef PairHash<blink::Member<blink::incremental_marking_test::Object>,
blink::WeakMember<blink::incremental_marking_test::Object>>
Hash;
};
template <>
struct IsTraceable<blink::incremental_marking_test::StrongWeakPair> {
static const bool value = IsTraceable<std::pair<
blink::Member<blink::incremental_marking_test::Object>,
blink::WeakMember<blink::incremental_marking_test::Object>>>::value;
};
} // namespace WTF
namespace blink {
namespace incremental_marking_test {
TEST(IncrementalMarkingTest, HeapHashSetStrongWeakPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashSet<StrongWeakPair> set;
{
// Only the strong field in the StrongWeakPair should be hit by the
// write barrier.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
set.insert(StrongWeakPair(obj1, obj2));
EXPECT_FALSE(obj2->IsMarked());
}
}
TEST(IncrementalMarkingTest, HeapLinkedHashSetStrongWeakPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapLinkedHashSet<StrongWeakPair> set;
{
// Only the strong field in the StrongWeakPair should be hit by the
// write barrier.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
set.insert(StrongWeakPair(obj1, obj2));
EXPECT_FALSE(obj2->IsMarked());
}
}
// =============================================================================
// HeapLinkedHashSet support. ==================================================
// =============================================================================
TEST(IncrementalMarkingTest, HeapLinkedHashSetInsert) {
Insert<HeapLinkedHashSet<Member<Object>>>();
InsertNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapLinkedHashSetCopy) {
Copy<HeapLinkedHashSet<Member<Object>>>();
CopyNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapLinkedHashSetMove) {
Move<HeapLinkedHashSet<Member<Object>>>();
MoveNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapLinkedHashSetSwap) {
Swap<HeapLinkedHashSet<Member<Object>>>();
SwapNoBarrier<HeapLinkedHashSet<WeakMember<Object>>>();
}
// =============================================================================
// HeapHashCountedSet support. =================================================
// =============================================================================
// HeapHashCountedSet does not support copy or move.
TEST(IncrementalMarkingTest, HeapHashCountedSetInsert) {
Insert<HeapHashCountedSet<Member<Object>>>();
InsertNoBarrier<HeapHashCountedSet<WeakMember<Object>>>();
}
TEST(IncrementalMarkingTest, HeapHashCountedSetSwap) {
// HeapHashCountedSet is not move constructible so we cannot use std::swap.
{
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashCountedSet<Member<Object>> container1;
container1.insert(obj1);
HeapHashCountedSet<Member<Object>> container2;
container2.insert(obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2});
container1.swap(container2);
}
}
{
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashCountedSet<WeakMember<Object>> container1;
container1.insert(obj1);
HeapHashCountedSet<WeakMember<Object>> container2;
container2.insert(obj2);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2});
container1.swap(container2);
}
}
}
// =============================================================================
// HeapTerminatedArray support. ================================================
// =============================================================================
class TerminatedArrayNode {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
TerminatedArrayNode(Object* obj) : obj_(obj), is_last_in_array_(false) {}
// TerminatedArray support.
bool IsLastInArray() const { return is_last_in_array_; }
void SetLastInArray(bool flag) { is_last_in_array_ = flag; }
void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); }
private:
Member<Object> obj_;
bool is_last_in_array_;
};
} // namespace incremental_marking_test
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::incremental_marking_test::TerminatedArrayNode);
namespace blink {
namespace incremental_marking_test {
TEST(IncrementalMarkingTest, HeapTerminatedArrayBuilder) {
Object* obj = Object::Create();
HeapTerminatedArray<TerminatedArrayNode>* array = nullptr;
{
// The builder allocates the backing store on Oilpans heap, effectively
// triggering a write barrier.
HeapTerminatedArrayBuilder<TerminatedArrayNode> builder(array);
builder.Grow(1);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
builder.Append(TerminatedArrayNode(obj));
}
array = builder.Release();
}
}
// =============================================================================
// HeapHashMap support. ========================================================
// =============================================================================
TEST(IncrementalMarkingTest, HeapHashMapInsertMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
map.insert(obj1, obj2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapInsertWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map;
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2});
map.insert(obj1, obj2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapInsertMemberWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, WeakMember<Object>> map;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
map.insert(obj1, obj2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapInsertWeakMemberMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<WeakMember<Object>, Member<Object>> map;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
map.insert(obj1, obj2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapSetMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
map.Set(obj1, obj2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapSetMemberUpdateValue) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
map.insert(obj1, obj2);
{
// Only |obj3| is newly added to |map|, so we only expect the barrier to
// fire on this one.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj3});
map.Set(obj1, obj3);
EXPECT_FALSE(HeapObjectHeader::FromPayload(obj1)->IsMarked());
EXPECT_FALSE(HeapObjectHeader::FromPayload(obj2)->IsMarked());
}
}
TEST(IncrementalMarkingTest, HeapHashMapIteratorChangeKey) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
map.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj3});
auto it = map.find(obj1);
EXPECT_NE(map.end(), it);
it->key = obj3;
}
}
TEST(IncrementalMarkingTest, HeapHashMapIteratorChangeValue) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
map.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj3});
auto it = map.find(obj1);
EXPECT_NE(map.end(), it);
it->value = obj3;
}
}
TEST(IncrementalMarkingTest, HeapHashMapCopyMemberMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
EXPECT_TRUE(map1.Contains(obj1));
HeapHashMap<Member<Object>, Member<Object>> map2(map1);
EXPECT_TRUE(map1.Contains(obj1));
EXPECT_TRUE(map2.Contains(obj1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapCopyWeakMemberWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2});
EXPECT_TRUE(map1.Contains(obj1));
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(map1);
EXPECT_TRUE(map1.Contains(obj1));
EXPECT_TRUE(map2.Contains(obj1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapCopyMemberWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, WeakMember<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
EXPECT_TRUE(map1.Contains(obj1));
HeapHashMap<Member<Object>, WeakMember<Object>> map2(map1);
EXPECT_TRUE(map1.Contains(obj1));
EXPECT_TRUE(map2.Contains(obj1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapCopyWeakMemberMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<WeakMember<Object>, Member<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
EXPECT_TRUE(map1.Contains(obj1));
HeapHashMap<WeakMember<Object>, Member<Object>> map2(map1);
EXPECT_TRUE(map1.Contains(obj1));
EXPECT_TRUE(map2.Contains(obj1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapMoveMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
HeapHashMap<Member<Object>, Member<Object>> map2(std::move(map1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapMoveWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2});
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(std::move(map1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapMoveMemberWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, WeakMember<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
HeapHashMap<Member<Object>, WeakMember<Object>> map2(std::move(map1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapMoveWeakMemberMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<WeakMember<Object>, Member<Object>> map1;
map1.insert(obj1, obj2);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
HeapHashMap<WeakMember<Object>, Member<Object>> map2(std::move(map1));
}
}
TEST(IncrementalMarkingTest, HeapHashMapSwapMemberMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
Object* obj4 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map1;
map1.insert(obj1, obj2);
HeapHashMap<Member<Object>, Member<Object>> map2;
map2.insert(obj3, obj4);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2, obj3, obj4});
std::swap(map1, map2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapSwapWeakMemberWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
Object* obj4 = Object::Create();
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
map1.insert(obj1, obj2);
HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2;
map2.insert(obj3, obj4);
{
ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
{obj1, obj2, obj3, obj4});
std::swap(map1, map2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapSwapMemberWeakMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
Object* obj4 = Object::Create();
HeapHashMap<Member<Object>, WeakMember<Object>> map1;
map1.insert(obj1, obj2);
HeapHashMap<Member<Object>, WeakMember<Object>> map2;
map2.insert(obj3, obj4);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj3});
std::swap(map1, map2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapSwapWeakMemberMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
Object* obj4 = Object::Create();
HeapHashMap<WeakMember<Object>, Member<Object>> map1;
map1.insert(obj1, obj2);
HeapHashMap<WeakMember<Object>, Member<Object>> map2;
map2.insert(obj3, obj4);
{
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2, obj4});
std::swap(map1, map2);
}
}
TEST(IncrementalMarkingTest, HeapHashMapInsertStrongWeakPairMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
HeapHashMap<StrongWeakPair, Member<Object>> map;
{
// Tests that the write barrier also fires for entities such as
// StrongWeakPair that don't overload assignment operators in translators.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj3});
map.insert(StrongWeakPair(obj1, obj2), obj3);
}
}
TEST(IncrementalMarkingTest, HeapHashMapInsertMemberStrongWeakPair) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
Object* obj3 = Object::Create();
HeapHashMap<Member<Object>, StrongWeakPair> map;
{
// Tests that the write barrier also fires for entities such as
// StrongWeakPair that don't overload assignment operators in translators.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
map.insert(obj1, StrongWeakPair(obj2, obj3));
}
}
TEST(IncrementalMarkingTest, HeapHashMapCopyKeysToVectorMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
map.insert(obj1, obj2);
HeapVector<Member<Object>> vec;
{
// Only key should have its write barrier fired. A write barrier call for
// value hints to an inefficient implementation.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
CopyKeysToVector(map, vec);
}
}
TEST(IncrementalMarkingTest, HeapHashMapCopyValuesToVectorMember) {
Object* obj1 = Object::Create();
Object* obj2 = Object::Create();
HeapHashMap<Member<Object>, Member<Object>> map;
map.insert(obj1, obj2);
HeapVector<Member<Object>> vec;
{
// Only value should have its write barrier fired. A write barrier call for
// key hints to an inefficient implementation.
ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
CopyValuesToVector(map, vec);
}
}
TEST(IncrementalMarkingTest, WeakHashMapPromptlyFreeDisabled) {
ThreadState* state = ThreadState::Current();
state->SetGCState(ThreadState::kIncrementalMarkingStartScheduled);
state->SetGCState(ThreadState::kIncrementalMarkingStepScheduled);
Persistent<Object> obj1 = Object::Create();
NormalPageArena* arena = static_cast<NormalPageArena*>(
ThreadState::Current()->Heap().Arena(BlinkGC::kHashTableArenaIndex));
CHECK(arena);
{
size_t before = arena->promptly_freed_size();
// Create two maps so we don't promptly free at the allocation point.
HeapHashMap<WeakMember<Object>, Member<Object>> weak_map1;
HeapHashMap<WeakMember<Object>, Member<Object>> weak_map2;
weak_map1.insert(obj1, obj1);
weak_map2.insert(obj1, obj1);
weak_map1.clear();
size_t after = arena->promptly_freed_size();
// Weak hash table backings should not be promptly freed.
EXPECT_EQ(after, before);
}
{
size_t before = arena->promptly_freed_size();
// Create two maps so we don't promptly free at the allocation point.
HeapHashMap<Member<Object>, Member<Object>> map1;
HeapHashMap<Member<Object>, Member<Object>> map2;
map1.insert(obj1, obj1);
map2.insert(obj1, obj1);
map1.clear();
size_t after = arena->promptly_freed_size();
// Non-weak hash table backings should be promptly freed.
EXPECT_GT(after, before);
}
state->SetGCState(ThreadState::kIncrementalMarkingFinalizeScheduled);
state->SetGCState(ThreadState::kNoGCScheduled);
}
} // namespace incremental_marking_test
} // namespace blink
#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)