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