Commit 660fb963 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[heap] Templatize worklist for arbitrary entry types

To be used with tuples of heap objects and sizes.

Bug: chromium:738865
Change-Id: I29d9cf98bb2097cc8e1616aaf19a251507ffbd9e
Reviewed-on: https://chromium-review.googlesource.com/559050Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46407}
parent 18e73287
...@@ -15,12 +15,12 @@ namespace internal { ...@@ -15,12 +15,12 @@ namespace internal {
class Heap; class Heap;
class Isolate; class Isolate;
template <int SEGMENT_SIZE> template <typename EntryType, int SEGMENT_SIZE>
class Worklist; class Worklist;
class ConcurrentMarking { class ConcurrentMarking {
public: public:
using MarkingWorklist = Worklist<64 /* segment size */>; using MarkingWorklist = Worklist<HeapObject*, 64 /* segment size */>;
ConcurrentMarking(Heap* heap, MarkingWorklist* shared_, ConcurrentMarking(Heap* heap, MarkingWorklist* shared_,
MarkingWorklist* bailout_); MarkingWorklist* bailout_);
......
...@@ -816,8 +816,8 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() { ...@@ -816,8 +816,8 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() {
Map* filler_map = heap_->one_pointer_filler_map(); Map* filler_map = heap_->one_pointer_filler_map();
marking_worklist()->Update([this, marking_worklist()->Update([this, filler_map](HeapObject* obj,
filler_map](HeapObject* obj) -> HeapObject* { HeapObject** out) -> bool {
DCHECK(obj->IsHeapObject()); DCHECK(obj->IsHeapObject());
// Only pointers to from space have to be updated. // Only pointers to from space have to be updated.
if (heap_->InFromSpace(obj)) { if (heap_->InFromSpace(obj)) {
...@@ -828,35 +828,44 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() { ...@@ -828,35 +828,44 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() {
// If these object are dead at scavenging time, their marking deque // If these object are dead at scavenging time, their marking deque
// entries will not point to forwarding addresses. Hence, we can discard // entries will not point to forwarding addresses. Hence, we can discard
// them. // them.
return nullptr; return false;
} }
HeapObject* dest = map_word.ToForwardingAddress(); HeapObject* dest = map_word.ToForwardingAddress();
DCHECK_IMPLIES( DCHECK_IMPLIES(
ObjectMarking::IsWhite<kAtomicity>(obj, marking_state(obj)), ObjectMarking::IsWhite<kAtomicity>(obj, marking_state(obj)),
obj->IsFiller()); obj->IsFiller());
return dest; *out = dest;
return true;
} else if (heap_->InToSpace(obj)) { } else if (heap_->InToSpace(obj)) {
// The object may be on a page that was moved in new space. // The object may be on a page that was moved in new space.
DCHECK( DCHECK(
Page::FromAddress(obj->address())->IsFlagSet(Page::SWEEP_TO_ITERATE)); Page::FromAddress(obj->address())->IsFlagSet(Page::SWEEP_TO_ITERATE));
return ObjectMarking::IsGrey<kAtomicity>(obj, MarkingState::External(obj)) if (ObjectMarking::IsGrey<kAtomicity>(obj, MarkingState::External(obj))) {
? obj *out = obj;
: nullptr; return true;
}
return false;
} else { } else {
// The object may be on a page that was moved from new to old space. // The object may be on a page that was moved from new to old space.
if (Page::FromAddress(obj->address()) if (Page::FromAddress(obj->address())
->IsFlagSet(Page::SWEEP_TO_ITERATE)) { ->IsFlagSet(Page::SWEEP_TO_ITERATE)) {
return ObjectMarking::IsGrey<kAtomicity>(obj, if (ObjectMarking::IsGrey<kAtomicity>(obj,
MarkingState::External(obj)) MarkingState::External(obj))) {
? obj *out = obj;
: nullptr; return true;
}
return false;
} }
DCHECK_IMPLIES( DCHECK_IMPLIES(
ObjectMarking::IsWhite<kAtomicity>(obj, marking_state(obj)), ObjectMarking::IsWhite<kAtomicity>(obj, marking_state(obj)),
obj->IsFiller()); obj->IsFiller());
// Skip one word filler objects that appear on the // Skip one word filler objects that appear on the
// stack when we perform in place array shift. // stack when we perform in place array shift.
return (obj->map() == filler_map) ? nullptr : obj; if (obj->map() != filler_map) {
*out = obj;
return true;
}
return false;
} }
}); });
} }
......
...@@ -24,10 +24,8 @@ class ItemParallelJob; ...@@ -24,10 +24,8 @@ class ItemParallelJob;
class MigrationObserver; class MigrationObserver;
class RecordMigratedSlotVisitor; class RecordMigratedSlotVisitor;
class YoungGenerationMarkingVisitor; class YoungGenerationMarkingVisitor;
template <int SEGMENT_SIZE> template <typename EntryType, int SEGMENT_SIZE>
class Worklist; class Worklist;
template <int SEGMENT_SIZE>
class WorklistView;
class ObjectMarking : public AllStatic { class ObjectMarking : public AllStatic {
public: public:
...@@ -350,7 +348,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase { ...@@ -350,7 +348,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
void CleanupSweepToIteratePages(); void CleanupSweepToIteratePages();
private: private:
using MarkingWorklist = Worklist<64 /* segment size */>; using MarkingWorklist = Worklist<HeapObject*, 64 /* segment size */>;
class RootMarkingVisitorSeedOnly; class RootMarkingVisitorSeedOnly;
class RootMarkingVisitor; class RootMarkingVisitor;
...@@ -396,7 +394,7 @@ class MarkCompactCollector final : public MarkCompactCollectorBase { ...@@ -396,7 +394,7 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
// Wrapper for the shared and bailout worklists. // Wrapper for the shared and bailout worklists.
class MarkingWorklist { class MarkingWorklist {
public: public:
using ConcurrentMarkingWorklist = Worklist<64>; using ConcurrentMarkingWorklist = Worklist<HeapObject*, 64>;
static const int kMainThread = 0; static const int kMainThread = 0;
// The heap parameter is not used but needed to match the sequential case. // The heap parameter is not used but needed to match the sequential case.
......
...@@ -87,9 +87,7 @@ class SequentialMarkingDeque { ...@@ -87,9 +87,7 @@ class SequentialMarkingDeque {
int i = bottom_; int i = bottom_;
int new_top = bottom_; int new_top = bottom_;
while (i != top_) { while (i != top_) {
HeapObject* object = callback(array_[i]); if (callback(array_[i], &array_[new_top])) {
if (object) {
array_[new_top] = object;
new_top = (new_top + 1) & mask_; new_top = (new_top + 1) & mask_;
} }
i = (i + 1) & mask_; i = (i + 1) & mask_;
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class HeapObject;
// A concurrent worklist based on segments. Each tasks gets private // A concurrent worklist based on segments. Each tasks gets private
// push and pop segments. Empty pop segments are swapped with their // push and pop segments. Empty pop segments are swapped with their
// corresponding push segments. Full push segments are published to a global // corresponding push segments. Full push segments are published to a global
...@@ -25,19 +23,19 @@ class HeapObject; ...@@ -25,19 +23,19 @@ class HeapObject;
// //
// Work stealing is best effort, i.e., there is no way to inform other tasks // Work stealing is best effort, i.e., there is no way to inform other tasks
// of the need of items. // of the need of items.
template <int SEGMENT_SIZE> template <typename EntryType, int SEGMENT_SIZE>
class Worklist { class Worklist {
public: public:
class View { class View {
public: public:
View(Worklist<SEGMENT_SIZE>* worklist, int task_id) View(Worklist<EntryType, SEGMENT_SIZE>* worklist, int task_id)
: worklist_(worklist), task_id_(task_id) {} : worklist_(worklist), task_id_(task_id) {}
// Pushes an object onto the worklist. // Pushes an entry onto the worklist.
bool Push(HeapObject* object) { return worklist_->Push(task_id_, object); } bool Push(EntryType entry) { return worklist_->Push(task_id_, entry); }
// Pops an object from the worklist. // Pops an entry from the worklist.
bool Pop(HeapObject** object) { return worklist_->Pop(task_id_, object); } bool Pop(EntryType* entry) { return worklist_->Pop(task_id_, entry); }
// Returns true if the local portion of the worklist is empty. // Returns true if the local portion of the worklist is empty.
bool IsLocalEmpty() { return worklist_->IsLocalEmpty(task_id_); } bool IsLocalEmpty() { return worklist_->IsLocalEmpty(task_id_); }
...@@ -47,7 +45,7 @@ class Worklist { ...@@ -47,7 +45,7 @@ class Worklist {
bool IsGlobalEmpty() { return worklist_->IsGlobalEmpty(); } bool IsGlobalEmpty() { return worklist_->IsGlobalEmpty(); }
private: private:
Worklist<SEGMENT_SIZE>* worklist_; Worklist<EntryType, SEGMENT_SIZE>* worklist_;
int task_id_; int task_id_;
}; };
...@@ -71,22 +69,22 @@ class Worklist { ...@@ -71,22 +69,22 @@ class Worklist {
} }
} }
bool Push(int task_id, HeapObject* object) { bool Push(int task_id, EntryType entry) {
DCHECK_LT(task_id, kMaxNumTasks); DCHECK_LT(task_id, kMaxNumTasks);
DCHECK_NOT_NULL(private_push_segment_[task_id]); DCHECK_NOT_NULL(private_push_segment_[task_id]);
if (!private_push_segment_[task_id]->Push(object)) { if (!private_push_segment_[task_id]->Push(entry)) {
PublishPushSegmentToGlobal(task_id); PublishPushSegmentToGlobal(task_id);
bool success = private_push_segment_[task_id]->Push(object); bool success = private_push_segment_[task_id]->Push(entry);
USE(success); USE(success);
DCHECK(success); DCHECK(success);
} }
return true; return true;
} }
bool Pop(int task_id, HeapObject** object) { bool Pop(int task_id, EntryType* entry) {
DCHECK_LT(task_id, kMaxNumTasks); DCHECK_LT(task_id, kMaxNumTasks);
DCHECK_NOT_NULL(private_pop_segment_[task_id]); DCHECK_NOT_NULL(private_pop_segment_[task_id]);
if (!private_pop_segment_[task_id]->Pop(object)) { if (!private_pop_segment_[task_id]->Pop(entry)) {
if (!private_push_segment_[task_id]->IsEmpty()) { if (!private_push_segment_[task_id]->IsEmpty()) {
Segment* tmp = private_pop_segment_[task_id]; Segment* tmp = private_pop_segment_[task_id];
private_pop_segment_[task_id] = private_push_segment_[task_id]; private_pop_segment_[task_id] = private_push_segment_[task_id];
...@@ -94,7 +92,7 @@ class Worklist { ...@@ -94,7 +92,7 @@ class Worklist {
} else if (!StealPopSegmentFromGlobal(task_id)) { } else if (!StealPopSegmentFromGlobal(task_id)) {
return false; return false;
} }
bool success = private_pop_segment_[task_id]->Pop(object); bool success = private_pop_segment_[task_id]->Pop(entry);
USE(success); USE(success);
DCHECK(success); DCHECK(success);
} }
...@@ -137,9 +135,11 @@ class Worklist { ...@@ -137,9 +135,11 @@ class Worklist {
} }
// Calls the specified callback on each element of the deques and replaces // Calls the specified callback on each element of the deques and replaces
// the element with the result of the callback. If the callback returns // the element with the result of the callback.
// nullptr then the element is removed from the worklist. // The signature of the callback is
// The callback must accept HeapObject* and return HeapObject*. // bool Callback(EntryType old, EntryType* new).
// If the callback returns |false| then the element is removed from the
// worklist. Otherwise the |new| entry is updated.
// This function assumes that other tasks are not running. // This function assumes that other tasks are not running.
template <typename Callback> template <typename Callback>
void Update(Callback callback) { void Update(Callback callback) {
...@@ -165,17 +165,16 @@ class Worklist { ...@@ -165,17 +165,16 @@ class Worklist {
} }
private: private:
using TestWorklist = Worklist<64>; FRIEND_TEST(WorkListTest, SegmentCreate);
FRIEND_TEST(TestWorklist, SegmentCreate); FRIEND_TEST(WorkListTest, SegmentPush);
FRIEND_TEST(TestWorklist, SegmentPush); FRIEND_TEST(WorkListTest, SegmentPushPop);
FRIEND_TEST(TestWorklist, SegmentPushPop); FRIEND_TEST(WorkListTest, SegmentIsEmpty);
FRIEND_TEST(TestWorklist, SegmentIsEmpty); FRIEND_TEST(WorkListTest, SegmentIsFull);
FRIEND_TEST(TestWorklist, SegmentIsFull); FRIEND_TEST(WorkListTest, SegmentClear);
FRIEND_TEST(TestWorklist, SegmentClear); FRIEND_TEST(WorkListTest, SegmentFullPushFails);
FRIEND_TEST(TestWorklist, SegmentFullPushFails); FRIEND_TEST(WorkListTest, SegmentEmptyPopFails);
FRIEND_TEST(TestWorklist, SegmentEmptyPopFails); FRIEND_TEST(WorkListTest, SegmentUpdateFalse);
FRIEND_TEST(TestWorklist, SegmentUpdateNull); FRIEND_TEST(WorkListTest, SegmentUpdate);
FRIEND_TEST(TestWorklist, SegmentUpdate);
class Segment { class Segment {
public: public:
...@@ -183,15 +182,15 @@ class Worklist { ...@@ -183,15 +182,15 @@ class Worklist {
Segment() : index_(0) {} Segment() : index_(0) {}
bool Push(HeapObject* object) { bool Push(EntryType entry) {
if (IsFull()) return false; if (IsFull()) return false;
objects_[index_++] = object; entries_[index_++] = entry;
return true; return true;
} }
bool Pop(HeapObject** object) { bool Pop(EntryType* entry) {
if (IsEmpty()) return false; if (IsEmpty()) return false;
*object = objects_[--index_]; *entry = entries_[--index_];
return true; return true;
} }
...@@ -204,9 +203,8 @@ class Worklist { ...@@ -204,9 +203,8 @@ class Worklist {
void Update(Callback callback) { void Update(Callback callback) {
size_t new_index = 0; size_t new_index = 0;
for (size_t i = 0; i < index_; i++) { for (size_t i = 0; i < index_; i++) {
HeapObject* object = callback(objects_[i]); if (callback(entries_[i], &entries_[new_index])) {
if (object) { new_index++;
objects_[new_index++] = object;
} }
} }
index_ = new_index; index_ = new_index;
...@@ -214,7 +212,7 @@ class Worklist { ...@@ -214,7 +212,7 @@ class Worklist {
private: private:
size_t index_; size_t index_;
HeapObject* objects_[kCapacity]; EntryType entries_[kCapacity];
}; };
// Do not inline the following functions as this would mean that vector fast // Do not inline the following functions as this would mean that vector fast
......
...@@ -9,43 +9,43 @@ ...@@ -9,43 +9,43 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
using TestWorklist = Worklist<64>; class SomeObject {};
class HeapObject {}; using TestWorklist = Worklist<SomeObject*, 64>;
TEST(TestWorklist, SegmentCreate) { TEST(WorkListTest, SegmentCreate) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
EXPECT_EQ(0u, segment.Size()); EXPECT_EQ(0u, segment.Size());
EXPECT_FALSE(segment.IsFull()); EXPECT_FALSE(segment.IsFull());
} }
TEST(TestWorklist, SegmentPush) { TEST(WorkListTest, SegmentPush) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_EQ(0u, segment.Size()); EXPECT_EQ(0u, segment.Size());
EXPECT_TRUE(segment.Push(nullptr)); EXPECT_TRUE(segment.Push(nullptr));
EXPECT_EQ(1u, segment.Size()); EXPECT_EQ(1u, segment.Size());
} }
TEST(TestWorklist, SegmentPushPop) { TEST(WorkListTest, SegmentPushPop) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.Push(nullptr)); EXPECT_TRUE(segment.Push(nullptr));
EXPECT_EQ(1u, segment.Size()); EXPECT_EQ(1u, segment.Size());
HeapObject dummy; SomeObject dummy;
HeapObject* object = &dummy; SomeObject* object = &dummy;
EXPECT_TRUE(segment.Pop(&object)); EXPECT_TRUE(segment.Pop(&object));
EXPECT_EQ(0u, segment.Size()); EXPECT_EQ(0u, segment.Size());
EXPECT_EQ(nullptr, object); EXPECT_EQ(nullptr, object);
} }
TEST(TestWorklist, SegmentIsEmpty) { TEST(WorkListTest, SegmentIsEmpty) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
EXPECT_TRUE(segment.Push(nullptr)); EXPECT_TRUE(segment.Push(nullptr));
EXPECT_FALSE(segment.IsEmpty()); EXPECT_FALSE(segment.IsEmpty());
} }
TEST(TestWorklist, SegmentIsFull) { TEST(WorkListTest, SegmentIsFull) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_FALSE(segment.IsFull()); EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) { for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
...@@ -54,7 +54,7 @@ TEST(TestWorklist, SegmentIsFull) { ...@@ -54,7 +54,7 @@ TEST(TestWorklist, SegmentIsFull) {
EXPECT_TRUE(segment.IsFull()); EXPECT_TRUE(segment.IsFull());
} }
TEST(TestWorklist, SegmentClear) { TEST(WorkListTest, SegmentClear) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.Push(nullptr)); EXPECT_TRUE(segment.Push(nullptr));
EXPECT_FALSE(segment.IsEmpty()); EXPECT_FALSE(segment.IsEmpty());
...@@ -65,7 +65,7 @@ TEST(TestWorklist, SegmentClear) { ...@@ -65,7 +65,7 @@ TEST(TestWorklist, SegmentClear) {
} }
} }
TEST(TestWorklist, SegmentFullPushFails) { TEST(WorkListTest, SegmentFullPushFails) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_FALSE(segment.IsFull()); EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) { for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
...@@ -75,60 +75,63 @@ TEST(TestWorklist, SegmentFullPushFails) { ...@@ -75,60 +75,63 @@ TEST(TestWorklist, SegmentFullPushFails) {
EXPECT_FALSE(segment.Push(nullptr)); EXPECT_FALSE(segment.Push(nullptr));
} }
TEST(TestWorklist, SegmentEmptyPopFails) { TEST(WorkListTest, SegmentEmptyPopFails) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
HeapObject* object; SomeObject* object;
EXPECT_FALSE(segment.Pop(&object)); EXPECT_FALSE(segment.Pop(&object));
} }
TEST(TestWorklist, SegmentUpdateNull) { TEST(WorkListTest, SegmentUpdateFalse) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
HeapObject* object; SomeObject* object;
object = reinterpret_cast<HeapObject*>(&object); object = reinterpret_cast<SomeObject*>(&object);
EXPECT_TRUE(segment.Push(object)); EXPECT_TRUE(segment.Push(object));
segment.Update([](HeapObject* object) { return nullptr; }); segment.Update([](SomeObject* object, SomeObject** out) { return false; });
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
} }
TEST(TestWorklist, SegmentUpdate) { TEST(WorkListTest, SegmentUpdate) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
HeapObject* objectA; SomeObject* objectA;
objectA = reinterpret_cast<HeapObject*>(&objectA); objectA = reinterpret_cast<SomeObject*>(&objectA);
HeapObject* objectB; SomeObject* objectB;
objectB = reinterpret_cast<HeapObject*>(&objectB); objectB = reinterpret_cast<SomeObject*>(&objectB);
EXPECT_TRUE(segment.Push(objectA)); EXPECT_TRUE(segment.Push(objectA));
segment.Update([objectB](HeapObject* object) { return objectB; }); segment.Update([objectB](SomeObject* object, SomeObject** out) {
HeapObject* object; *out = objectB;
return true;
});
SomeObject* object;
EXPECT_TRUE(segment.Pop(&object)); EXPECT_TRUE(segment.Pop(&object));
EXPECT_EQ(object, objectB); EXPECT_EQ(object, objectB);
} }
TEST(TestWorklist, CreateEmpty) { TEST(WorkListTest, CreateEmpty) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view(&worklist, 0); TestWorklist::View worklist_view(&worklist, 0);
EXPECT_TRUE(worklist_view.IsLocalEmpty()); EXPECT_TRUE(worklist_view.IsLocalEmpty());
EXPECT_TRUE(worklist.IsGlobalEmpty()); EXPECT_TRUE(worklist.IsGlobalEmpty());
} }
TEST(TestWorklist, LocalPushPop) { TEST(WorkListTest, LocalPushPop) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view(&worklist, 0); TestWorklist::View worklist_view(&worklist, 0);
HeapObject dummy; SomeObject dummy;
HeapObject* retrieved = nullptr; SomeObject* retrieved = nullptr;
EXPECT_TRUE(worklist_view.Push(&dummy)); EXPECT_TRUE(worklist_view.Push(&dummy));
EXPECT_FALSE(worklist_view.IsLocalEmpty()); EXPECT_FALSE(worklist_view.IsLocalEmpty());
EXPECT_TRUE(worklist_view.Pop(&retrieved)); EXPECT_TRUE(worklist_view.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved); EXPECT_EQ(&dummy, retrieved);
} }
TEST(TestWorklist, LocalIsBasedOnId) { TEST(WorkListTest, LocalIsBasedOnId) {
TestWorklist worklist; TestWorklist worklist;
// Use the same id. // Use the same id.
TestWorklist::View worklist_view1(&worklist, 0); TestWorklist::View worklist_view1(&worklist, 0);
TestWorklist::View worklist_view2(&worklist, 0); TestWorklist::View worklist_view2(&worklist, 0);
HeapObject dummy; SomeObject dummy;
HeapObject* retrieved = nullptr; SomeObject* retrieved = nullptr;
EXPECT_TRUE(worklist_view1.Push(&dummy)); EXPECT_TRUE(worklist_view1.Push(&dummy));
EXPECT_FALSE(worklist_view1.IsLocalEmpty()); EXPECT_FALSE(worklist_view1.IsLocalEmpty());
EXPECT_FALSE(worklist_view2.IsLocalEmpty()); EXPECT_FALSE(worklist_view2.IsLocalEmpty());
...@@ -138,12 +141,12 @@ TEST(TestWorklist, LocalIsBasedOnId) { ...@@ -138,12 +141,12 @@ TEST(TestWorklist, LocalIsBasedOnId) {
EXPECT_TRUE(worklist_view2.IsLocalEmpty()); EXPECT_TRUE(worklist_view2.IsLocalEmpty());
} }
TEST(TestWorklist, LocalPushStaysPrivate) { TEST(WorkListTest, LocalPushStaysPrivate) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view1(&worklist, 0); TestWorklist::View worklist_view1(&worklist, 0);
TestWorklist::View worklist_view2(&worklist, 1); TestWorklist::View worklist_view2(&worklist, 1);
HeapObject dummy; SomeObject dummy;
HeapObject* retrieved = nullptr; SomeObject* retrieved = nullptr;
EXPECT_TRUE(worklist.IsGlobalEmpty()); EXPECT_TRUE(worklist.IsGlobalEmpty());
EXPECT_TRUE(worklist_view1.Push(&dummy)); EXPECT_TRUE(worklist_view1.Push(&dummy));
EXPECT_FALSE(worklist.IsGlobalEmpty()); EXPECT_FALSE(worklist.IsGlobalEmpty());
...@@ -154,28 +157,28 @@ TEST(TestWorklist, LocalPushStaysPrivate) { ...@@ -154,28 +157,28 @@ TEST(TestWorklist, LocalPushStaysPrivate) {
EXPECT_TRUE(worklist.IsGlobalEmpty()); EXPECT_TRUE(worklist.IsGlobalEmpty());
} }
TEST(TestWorklist, GlobalUpdateNull) { TEST(WorkListTest, GlobalUpdateNull) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view(&worklist, 0); TestWorklist::View worklist_view(&worklist, 0);
HeapObject* object; SomeObject* object;
object = reinterpret_cast<HeapObject*>(&object); object = reinterpret_cast<SomeObject*>(&object);
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view.Push(object)); EXPECT_TRUE(worklist_view.Push(object));
} }
EXPECT_TRUE(worklist_view.Push(object)); EXPECT_TRUE(worklist_view.Push(object));
worklist.Update([](HeapObject* object) { return nullptr; }); worklist.Update([](SomeObject* object, SomeObject** out) { return false; });
EXPECT_TRUE(worklist.IsGlobalEmpty()); EXPECT_TRUE(worklist.IsGlobalEmpty());
} }
TEST(TestWorklist, GlobalUpdate) { TEST(WorkListTest, GlobalUpdate) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view(&worklist, 0); TestWorklist::View worklist_view(&worklist, 0);
HeapObject* objectA = nullptr; SomeObject* objectA = nullptr;
objectA = reinterpret_cast<HeapObject*>(&objectA); objectA = reinterpret_cast<SomeObject*>(&objectA);
HeapObject* objectB = nullptr; SomeObject* objectB = nullptr;
objectB = reinterpret_cast<HeapObject*>(&objectB); objectB = reinterpret_cast<SomeObject*>(&objectB);
HeapObject* objectC = nullptr; SomeObject* objectC = nullptr;
objectC = reinterpret_cast<HeapObject*>(&objectC); objectC = reinterpret_cast<SomeObject*>(&objectC);
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view.Push(objectA)); EXPECT_TRUE(worklist_view.Push(objectA));
} }
...@@ -183,35 +186,39 @@ TEST(TestWorklist, GlobalUpdate) { ...@@ -183,35 +186,39 @@ TEST(TestWorklist, GlobalUpdate) {
EXPECT_TRUE(worklist_view.Push(objectB)); EXPECT_TRUE(worklist_view.Push(objectB));
} }
EXPECT_TRUE(worklist_view.Push(objectA)); EXPECT_TRUE(worklist_view.Push(objectA));
worklist.Update([objectA, objectC](HeapObject* object) { worklist.Update([objectA, objectC](SomeObject* object, SomeObject** out) {
return (object == objectA) ? nullptr : objectC; if (object != objectA) {
*out = objectC;
return true;
}
return false;
}); });
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
HeapObject* object; SomeObject* object;
EXPECT_TRUE(worklist_view.Pop(&object)); EXPECT_TRUE(worklist_view.Pop(&object));
EXPECT_EQ(object, objectC); EXPECT_EQ(object, objectC);
} }
} }
TEST(TestWorklist, FlushToGlobalPushSegment) { TEST(WorkListTest, FlushToGlobalPushSegment) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view0(&worklist, 0); TestWorklist::View worklist_view0(&worklist, 0);
TestWorklist::View worklist_view1(&worklist, 1); TestWorklist::View worklist_view1(&worklist, 1);
HeapObject* object = nullptr; SomeObject* object = nullptr;
HeapObject* objectA = nullptr; SomeObject* objectA = nullptr;
objectA = reinterpret_cast<HeapObject*>(&objectA); objectA = reinterpret_cast<SomeObject*>(&objectA);
EXPECT_TRUE(worklist_view0.Push(objectA)); EXPECT_TRUE(worklist_view0.Push(objectA));
worklist.FlushToGlobal(0); worklist.FlushToGlobal(0);
EXPECT_TRUE(worklist_view1.Pop(&object)); EXPECT_TRUE(worklist_view1.Pop(&object));
} }
TEST(TestWorklist, FlushToGlobalPopSegment) { TEST(WorkListTest, FlushToGlobalPopSegment) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view0(&worklist, 0); TestWorklist::View worklist_view0(&worklist, 0);
TestWorklist::View worklist_view1(&worklist, 1); TestWorklist::View worklist_view1(&worklist, 1);
HeapObject* object = nullptr; SomeObject* object = nullptr;
HeapObject* objectA = nullptr; SomeObject* objectA = nullptr;
objectA = reinterpret_cast<HeapObject*>(&objectA); objectA = reinterpret_cast<SomeObject*>(&objectA);
EXPECT_TRUE(worklist_view0.Push(objectA)); EXPECT_TRUE(worklist_view0.Push(objectA));
EXPECT_TRUE(worklist_view0.Push(objectA)); EXPECT_TRUE(worklist_view0.Push(objectA));
EXPECT_TRUE(worklist_view0.Pop(&object)); EXPECT_TRUE(worklist_view0.Pop(&object));
...@@ -219,11 +226,11 @@ TEST(TestWorklist, FlushToGlobalPopSegment) { ...@@ -219,11 +226,11 @@ TEST(TestWorklist, FlushToGlobalPopSegment) {
EXPECT_TRUE(worklist_view1.Pop(&object)); EXPECT_TRUE(worklist_view1.Pop(&object));
} }
TEST(TestWorklist, Clear) { TEST(WorkListTest, Clear) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view(&worklist, 0); TestWorklist::View worklist_view(&worklist, 0);
HeapObject* object; SomeObject* object;
object = reinterpret_cast<HeapObject*>(&object); object = reinterpret_cast<SomeObject*>(&object);
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view.Push(object)); EXPECT_TRUE(worklist_view.Push(object));
} }
...@@ -232,15 +239,15 @@ TEST(TestWorklist, Clear) { ...@@ -232,15 +239,15 @@ TEST(TestWorklist, Clear) {
EXPECT_TRUE(worklist.IsGlobalEmpty()); EXPECT_TRUE(worklist.IsGlobalEmpty());
} }
TEST(TestWorklist, SingleSegmentSteal) { TEST(WorkListTest, SingleSegmentSteal) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view1(&worklist, 0); TestWorklist::View worklist_view1(&worklist, 0);
TestWorklist::View worklist_view2(&worklist, 1); TestWorklist::View worklist_view2(&worklist, 1);
HeapObject dummy; SomeObject dummy;
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view1.Push(&dummy)); EXPECT_TRUE(worklist_view1.Push(&dummy));
} }
HeapObject* retrieved = nullptr; SomeObject* retrieved = nullptr;
// One more push/pop to publish the full segment. // One more push/pop to publish the full segment.
EXPECT_TRUE(worklist_view1.Push(nullptr)); EXPECT_TRUE(worklist_view1.Push(nullptr));
EXPECT_TRUE(worklist_view1.Pop(&retrieved)); EXPECT_TRUE(worklist_view1.Pop(&retrieved));
...@@ -254,30 +261,30 @@ TEST(TestWorklist, SingleSegmentSteal) { ...@@ -254,30 +261,30 @@ TEST(TestWorklist, SingleSegmentSteal) {
EXPECT_TRUE(worklist.IsGlobalEmpty()); EXPECT_TRUE(worklist.IsGlobalEmpty());
} }
TEST(TestWorklist, MultipleSegmentsStolen) { TEST(WorkListTest, MultipleSegmentsStolen) {
TestWorklist worklist; TestWorklist worklist;
TestWorklist::View worklist_view1(&worklist, 0); TestWorklist::View worklist_view1(&worklist, 0);
TestWorklist::View worklist_view2(&worklist, 1); TestWorklist::View worklist_view2(&worklist, 1);
TestWorklist::View worklist_view3(&worklist, 2); TestWorklist::View worklist_view3(&worklist, 2);
HeapObject dummy1; SomeObject dummy1;
HeapObject dummy2; SomeObject dummy2;
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view1.Push(&dummy1)); EXPECT_TRUE(worklist_view1.Push(&dummy1));
} }
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view1.Push(&dummy2)); EXPECT_TRUE(worklist_view1.Push(&dummy2));
} }
HeapObject* retrieved = nullptr; SomeObject* retrieved = nullptr;
HeapObject dummy3; SomeObject dummy3;
// One more push/pop to publish the full segment. // One more push/pop to publish the full segment.
EXPECT_TRUE(worklist_view1.Push(&dummy3)); EXPECT_TRUE(worklist_view1.Push(&dummy3));
EXPECT_TRUE(worklist_view1.Pop(&retrieved)); EXPECT_TRUE(worklist_view1.Pop(&retrieved));
EXPECT_EQ(&dummy3, retrieved); EXPECT_EQ(&dummy3, retrieved);
// Stealing. // Stealing.
EXPECT_TRUE(worklist_view2.Pop(&retrieved)); EXPECT_TRUE(worklist_view2.Pop(&retrieved));
HeapObject* const expect_bag2 = retrieved; SomeObject* const expect_bag2 = retrieved;
EXPECT_TRUE(worklist_view3.Pop(&retrieved)); EXPECT_TRUE(worklist_view3.Pop(&retrieved));
HeapObject* const expect_bag3 = retrieved; SomeObject* const expect_bag3 = retrieved;
EXPECT_NE(expect_bag2, expect_bag3); EXPECT_NE(expect_bag2, expect_bag3);
EXPECT_TRUE(expect_bag2 == &dummy1 || expect_bag2 == &dummy2); EXPECT_TRUE(expect_bag2 == &dummy1 || expect_bag2 == &dummy2);
EXPECT_TRUE(expect_bag3 == &dummy1 || expect_bag3 == &dummy2); EXPECT_TRUE(expect_bag3 == &dummy1 || expect_bag3 == &dummy2);
......
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