Commit baf95475 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[heap] Implement workstealing bag based on segments

Bug: chromium:651354
Change-Id: I8aa122f48986f494146d4e896b254846de7ce295
Reviewed-on: https://chromium-review.googlesource.com/543500
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46119}
parent c4852ea9
......@@ -1652,7 +1652,7 @@ v8_source_set("v8_base") {
"src/heap/spaces.h",
"src/heap/store-buffer.cc",
"src/heap/store-buffer.h",
"src/heap/workstealing-marking-deque.h",
"src/heap/workstealing-bag.h",
"src/ic/access-compiler-data.h",
"src/ic/access-compiler.cc",
"src/ic/access-compiler.h",
......
......@@ -27,7 +27,7 @@
#include "src/heap/objects-visiting.h"
#include "src/heap/page-parallel-job.h"
#include "src/heap/spaces-inl.h"
#include "src/heap/workstealing-marking-deque.h"
#include "src/heap/workstealing-bag.h"
#include "src/ic/ic.h"
#include "src/ic/stub-cache.h"
#include "src/tracing/tracing-category-observer.h"
......@@ -2210,7 +2210,7 @@ class YoungGenerationMarkingVisitor final
using BaseClass = HeapVisitor<int, YoungGenerationMarkingVisitor>;
YoungGenerationMarkingVisitor(Heap* heap,
WorkStealingMarkingDeque* global_marking_deque,
WorkStealingBag* global_marking_deque,
int task_id)
: heap_(heap), marking_deque_(global_marking_deque, task_id) {}
......@@ -2303,7 +2303,7 @@ class YoungGenerationMarkingVisitor final
}
Heap* heap_;
LocalWorkStealingMarkingDeque marking_deque_;
LocalWorkStealingBag marking_deque_;
};
class MinorMarkCompactCollector::RootMarkingVisitor : public RootVisitor {
......@@ -2359,8 +2359,7 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
public:
YoungGenerationMarkingTask(Isolate* isolate,
MinorMarkCompactCollector* collector,
WorkStealingMarkingDeque* marking_deque,
int task_id)
WorkStealingBag* marking_deque, int task_id)
: ItemParallelJob::Task(isolate),
collector_(collector),
marking_deque_(marking_deque, task_id),
......@@ -2380,7 +2379,7 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
EmptyLocalMarkingDeque();
}
EmptyMarkingDeque();
DCHECK(marking_deque_.IsEmpty());
DCHECK(marking_deque_.IsLocalEmpty());
FlushLiveBytes();
}
if (FLAG_trace_minor_mc_parallel_marking) {
......@@ -2414,11 +2413,9 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
void EmptyMarkingDeque() {
HeapObject* object = nullptr;
while (marking_deque_.WaitForMoreObjects()) {
while (marking_deque_.Pop(&object)) {
const int size = visitor_.Visit(object);
IncrementLiveBytes(object, size);
}
while (marking_deque_.Pop(&object)) {
const int size = visitor_.Visit(object);
IncrementLiveBytes(object, size);
}
}
......@@ -2435,7 +2432,7 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
}
MinorMarkCompactCollector* collector_;
LocalWorkStealingMarkingDeque marking_deque_;
LocalWorkStealingBag marking_deque_;
YoungGenerationMarkingVisitor visitor_;
std::unordered_map<Page*, intptr_t, Page::Hasher> local_live_bytes_;
};
......@@ -2599,11 +2596,11 @@ class MinorMarkCompactCollector::RootMarkingVisitorSeedOnly
MinorMarkCompactCollector::MinorMarkCompactCollector(Heap* heap)
: MarkCompactCollectorBase(heap),
marking_deque_(new WorkStealingMarkingDeque()),
marking_deque_(new WorkStealingBag()),
main_marking_visitor_(
new YoungGenerationMarkingVisitor(heap, marking_deque_, kMainMarker)),
page_parallel_job_semaphore_(0) {
static_assert(kNumMarkers <= WorkStealingMarkingDeque::kMaxNumTasks,
static_assert(kNumMarkers <= WorkStealingBag::kMaxNumTasks,
"more marker tasks than marking deque can handle");
}
......@@ -2668,6 +2665,7 @@ void MinorMarkCompactCollector::MarkRootSetInParallel() {
marking_deque(), i));
}
job.Run();
DCHECK(marking_deque()->IsGlobalEmpty());
}
}
old_to_new_slots_ = static_cast<int>(slots.Value());
......@@ -2704,8 +2702,7 @@ void MinorMarkCompactCollector::ProcessMarkingDeque() {
}
void MinorMarkCompactCollector::EmptyMarkingDeque() {
LocalWorkStealingMarkingDeque local_marking_deque(marking_deque(),
kMainMarker);
LocalWorkStealingBag local_marking_deque(marking_deque(), kMainMarker);
HeapObject* object = nullptr;
while (local_marking_deque.Pop(&object)) {
DCHECK(!object->IsFiller());
......@@ -2717,7 +2714,7 @@ void MinorMarkCompactCollector::EmptyMarkingDeque() {
object, marking_state(object))));
main_marking_visitor()->Visit(object);
}
DCHECK(local_marking_deque.IsEmpty());
DCHECK(local_marking_deque.IsLocalEmpty());
}
void MinorMarkCompactCollector::CollectGarbage() {
......
......@@ -23,7 +23,7 @@ namespace internal {
class EvacuationJobTraits;
class HeapObjectVisitor;
class ItemParallelJob;
class LocalWorkStealingMarkingDeque;
class LocalWorkStealingBag;
class MarkCompactCollector;
class MinorMarkCompactCollector;
class MarkingVisitor;
......@@ -32,7 +32,7 @@ template <typename JobTraits>
class PageParallelJob;
class RecordMigratedSlotVisitor;
class ThreadLocalTop;
class WorkStealingMarkingDeque;
class WorkStealingBag;
class YoungGenerationMarkingVisitor;
#ifdef V8_CONCURRENT_MARKING
......@@ -354,7 +354,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
static const int kNumMarkers = 8;
static const int kMainMarker = 0;
inline WorkStealingMarkingDeque* marking_deque() { return marking_deque_; }
inline WorkStealingBag* marking_deque() { return marking_deque_; }
inline YoungGenerationMarkingVisitor* main_marking_visitor() {
return main_marking_visitor_;
......@@ -374,7 +374,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
int NumberOfParallelMarkingTasks(int pages);
WorkStealingMarkingDeque* marking_deque_;
WorkStealingBag* marking_deque_;
YoungGenerationMarkingVisitor* main_marking_visitor_;
base::Semaphore page_parallel_job_semaphore_;
List<Page*> new_space_evacuation_pages_;
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_HEAP_WORKSTEALING_BAG_
#define V8_HEAP_WORKSTEALING_BAG_
#include <cstddef>
#include <vector>
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
namespace v8 {
namespace internal {
class HeapObject;
// A concurrent work stealing bag 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
// pool of segments and replaced with empty segments.
//
// Work stealing is best effort, i.e., there is no way to inform other tasks
// of the need of items.
class WorkStealingBag {
public:
static const int kMaxNumTasks = 8;
static const int kSegmentCapacity = 64;
WorkStealingBag() {
for (int i = 0; i < kMaxNumTasks; i++) {
private_push_segment_[i] = new Segment();
private_pop_segment_[i] = new Segment();
}
}
~WorkStealingBag() {
CHECK(IsGlobalEmpty());
for (int i = 0; i < kMaxNumTasks; i++) {
DCHECK_NOT_NULL(private_push_segment_[i]);
DCHECK_NOT_NULL(private_pop_segment_[i]);
delete private_push_segment_[i];
delete private_pop_segment_[i];
}
}
bool Push(int task_id, HeapObject* object) {
DCHECK_LT(task_id, kMaxNumTasks);
DCHECK_NOT_NULL(private_push_segment_[task_id]);
if (!private_push_segment_[task_id]->Push(object)) {
base::LockGuard<base::Mutex> guard(&lock_);
global_pool_.push_back(private_push_segment_[task_id]);
private_push_segment_[task_id] = new Segment();
bool success = private_push_segment_[task_id]->Push(object);
USE(success);
DCHECK(success);
}
return true;
}
bool Pop(int task_id, HeapObject** object) {
DCHECK_LT(task_id, kMaxNumTasks);
DCHECK_NOT_NULL(private_pop_segment_[task_id]);
if (!private_pop_segment_[task_id]->Pop(object)) {
if (!private_push_segment_[task_id]->IsEmpty()) {
Segment* tmp = private_pop_segment_[task_id];
private_pop_segment_[task_id] = private_push_segment_[task_id];
private_push_segment_[task_id] = tmp;
} else {
base::LockGuard<base::Mutex> guard(&lock_);
if (global_pool_.empty()) return false;
delete private_pop_segment_[task_id];
private_pop_segment_[task_id] = global_pool_.back();
global_pool_.pop_back();
}
bool success = private_pop_segment_[task_id]->Pop(object);
USE(success);
DCHECK(success);
}
return true;
}
bool IsLocalEmpty(int task_id) {
return private_pop_segment_[task_id]->IsEmpty() &&
private_push_segment_[task_id]->IsEmpty();
}
bool IsGlobalEmpty() {
for (int i = 0; i < kMaxNumTasks; i++) {
if (!IsLocalEmpty(i)) return false;
}
return global_pool_.empty();
}
private:
FRIEND_TEST(WorkStealingBag, SegmentCreate);
FRIEND_TEST(WorkStealingBag, SegmentPush);
FRIEND_TEST(WorkStealingBag, SegmentPushPop);
FRIEND_TEST(WorkStealingBag, SegmentIsEmpty);
FRIEND_TEST(WorkStealingBag, SegmentIsFull);
FRIEND_TEST(WorkStealingBag, SegmentClear);
FRIEND_TEST(WorkStealingBag, SegmentFullPushFails);
FRIEND_TEST(WorkStealingBag, SegmentEmptyPopFails);
class Segment {
public:
static const int kCapacity = kSegmentCapacity;
Segment() : index_(0) {}
bool Push(HeapObject* object) {
if (IsFull()) return false;
objects_[index_++] = object;
return true;
}
bool Pop(HeapObject** object) {
if (IsEmpty()) return false;
*object = objects_[--index_];
return true;
}
size_t Size() { return index_; }
bool IsEmpty() { return index_ == 0; }
bool IsFull() { return index_ == kCapacity; }
void Clear() { index_ = 0; }
private:
size_t index_;
HeapObject* objects_[kCapacity];
};
base::Mutex lock_;
Segment* private_pop_segment_[kMaxNumTasks];
Segment* private_push_segment_[kMaxNumTasks];
std::vector<Segment*> global_pool_;
};
class LocalWorkStealingBag {
public:
LocalWorkStealingBag(WorkStealingBag* bag, int task_id)
: bag_(bag), task_id_(task_id) {}
// Pushes an object onto the bag.
bool Push(HeapObject* object) { return bag_->Push(task_id_, object); }
// Pops an object from the bag.
bool Pop(HeapObject** object) { return bag_->Pop(task_id_, object); }
// Returns true if the local portion of the bag is empty.
bool IsLocalEmpty() { return bag_->IsLocalEmpty(task_id_); }
// Returns true if the bag is empty. Can only be used from the main thread
// without concurrent access.
bool IsGlobalEmpty() { return bag_->IsGlobalEmpty(); }
private:
WorkStealingBag* bag_;
int task_id_;
};
} // namespace internal
} // namespace v8
#endif // V8_HEAP_WORKSTEALING_BAG_
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_HEAP_WORKSTEALING_MARKING_DEQUE_
#define V8_HEAP_WORKSTEALING_MARKING_DEQUE_
#include <cstddef>
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace internal {
class HeapObject;
class StackSegment {
public:
static const int kNumEntries = 64;
StackSegment(StackSegment* next, StackSegment* prev)
: next_(next), prev_(prev), index_(0) {}
bool Push(HeapObject* object) {
if (IsFull()) return false;
objects_[index_++] = object;
return true;
}
bool Pop(HeapObject** object) {
if (IsEmpty()) return false;
*object = objects_[--index_];
return true;
}
size_t Size() { return index_; }
bool IsEmpty() { return index_ == 0; }
bool IsFull() { return index_ == kNumEntries; }
void Clear() { index_ = 0; }
StackSegment* next() { return next_; }
StackSegment* prev() { return prev_; }
void set_next(StackSegment* next) { next_ = next; }
void set_prev(StackSegment* prev) { prev_ = prev; }
void Unlink() {
if (next() != nullptr) next()->set_prev(prev());
if (prev() != nullptr) prev()->set_next(next());
}
private:
StackSegment* next_;
StackSegment* prev_;
size_t index_;
HeapObject* objects_[kNumEntries];
};
class SegmentedStack {
public:
SegmentedStack()
: front_(new StackSegment(nullptr, nullptr)), back_(front_) {}
~SegmentedStack() {
CHECK(IsEmpty());
delete front_;
}
bool Push(HeapObject* object) {
if (!front_->Push(object)) {
NewFront();
bool success = front_->Push(object);
USE(success);
DCHECK(success);
}
return true;
}
bool Pop(HeapObject** object) {
if (!front_->Pop(object)) {
if (IsEmpty()) return false;
DeleteFront();
bool success = front_->Pop(object);
USE(success);
DCHECK(success);
}
return object;
}
bool IsEmpty() { return front_ == back_ && front_->IsEmpty(); }
private:
void NewFront() {
StackSegment* s = new StackSegment(front_, nullptr);
front_->set_prev(s);
front_ = s;
}
void DeleteFront() { delete Unlink(front_); }
StackSegment* Unlink(StackSegment* segment) {
CHECK_NE(front_, back_);
if (segment == front_) front_ = front_->next();
if (segment == back_) back_ = back_->prev();
segment->Unlink();
return segment;
}
StackSegment* front_;
StackSegment* back_;
};
// TODO(mlippautz): Implement actual work stealing.
class WorkStealingMarkingDeque {
public:
static const int kMaxNumTasks = 8;
bool Push(int task_id, HeapObject* object) {
DCHECK_LT(task_id, kMaxNumTasks);
return private_stacks_[task_id].Push(object);
}
bool Pop(int task_id, HeapObject** object) {
DCHECK_LT(task_id, kMaxNumTasks);
return private_stacks_[task_id].Pop(object);
}
bool IsLocalEmpty(int task_id) { return private_stacks_[task_id].IsEmpty(); }
private:
SegmentedStack private_stacks_[kMaxNumTasks];
};
class LocalWorkStealingMarkingDeque {
public:
LocalWorkStealingMarkingDeque(WorkStealingMarkingDeque* deque, int task_id)
: deque_(deque), task_id_(task_id) {}
// Pushes an object onto the marking deque.
bool Push(HeapObject* object) { return deque_->Push(task_id_, object); }
// Pops an object onto the marking deque.
bool Pop(HeapObject** object) { return deque_->Pop(task_id_, object); }
// Returns true if the local portion of the marking deque is empty.
bool IsEmpty() { return deque_->IsLocalEmpty(task_id_); }
// Blocks if there are no more objects available. Returns execution with
// |true| once new objects are available and |false| otherwise.
bool WaitForMoreObjects() {
// Return false once the local portion of the marking deque is drained.
// TODO(mlippautz): Implement a barrier that can be used to synchronize
// work stealing and emptiness.
return !IsEmpty();
}
private:
WorkStealingMarkingDeque* deque_;
int task_id_;
};
} // namespace internal
} // namespace v8
#endif // V8_HEAP_WORKSTEALING_MARKING_DEQUE_
......@@ -1109,7 +1109,7 @@
'heap/spaces.h',
'heap/store-buffer.cc',
'heap/store-buffer.h',
'heap/workstealing-marking-deque.h',
'heap/workstealing-bag.h',
'intl.cc',
'intl.h',
'icu_util.cc',
......
......@@ -113,7 +113,7 @@ v8_executable("unittests") {
"heap/slot-set-unittest.cc",
"heap/spaces-unittest.cc",
"heap/unmapper-unittest.cc",
"heap/workstealing-marking-deque-unittest.cc",
"heap/workstealing-bag-unittest.cc",
"interpreter/bytecode-array-builder-unittest.cc",
"interpreter/bytecode-array-iterator-unittest.cc",
"interpreter/bytecode-array-random-iterator-unittest.cc",
......
// Copyright 2017 the V8 project 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 "src/heap/workstealing-bag.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
class HeapObject {};
TEST(WorkStealingBag, SegmentCreate) {
WorkStealingBag::Segment segment;
EXPECT_TRUE(segment.IsEmpty());
EXPECT_EQ(0u, segment.Size());
EXPECT_FALSE(segment.IsFull());
}
TEST(WorkStealingBag, SegmentPush) {
WorkStealingBag::Segment segment;
EXPECT_EQ(0u, segment.Size());
EXPECT_TRUE(segment.Push(nullptr));
EXPECT_EQ(1u, segment.Size());
}
TEST(WorkStealingBag, SegmentPushPop) {
WorkStealingBag::Segment segment;
EXPECT_TRUE(segment.Push(nullptr));
EXPECT_EQ(1u, segment.Size());
HeapObject dummy;
HeapObject* object = &dummy;
EXPECT_TRUE(segment.Pop(&object));
EXPECT_EQ(0u, segment.Size());
EXPECT_EQ(nullptr, object);
}
TEST(WorkStealingBag, SegmentIsEmpty) {
WorkStealingBag::Segment segment;
EXPECT_TRUE(segment.IsEmpty());
EXPECT_TRUE(segment.Push(nullptr));
EXPECT_FALSE(segment.IsEmpty());
}
TEST(WorkStealingBag, SegmentIsFull) {
WorkStealingBag::Segment segment;
EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < WorkStealingBag::Segment::kCapacity; i++) {
EXPECT_TRUE(segment.Push(nullptr));
}
EXPECT_TRUE(segment.IsFull());
}
TEST(WorkStealingBag, SegmentClear) {
WorkStealingBag::Segment segment;
EXPECT_TRUE(segment.Push(nullptr));
EXPECT_FALSE(segment.IsEmpty());
segment.Clear();
EXPECT_TRUE(segment.IsEmpty());
for (size_t i = 0; i < WorkStealingBag::Segment::kCapacity; i++) {
EXPECT_TRUE(segment.Push(nullptr));
}
}
TEST(WorkStealingBag, SegmentFullPushFails) {
WorkStealingBag::Segment segment;
EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < WorkStealingBag::Segment::kCapacity; i++) {
EXPECT_TRUE(segment.Push(nullptr));
}
EXPECT_TRUE(segment.IsFull());
EXPECT_FALSE(segment.Push(nullptr));
}
TEST(WorkStealingBag, SegmentEmptyPopFails) {
WorkStealingBag::Segment segment;
EXPECT_TRUE(segment.IsEmpty());
HeapObject* object;
EXPECT_FALSE(segment.Pop(&object));
}
TEST(WorkStealingBag, CreateEmpty) {
WorkStealingBag marking_bag;
LocalWorkStealingBag local_marking_bag(&marking_bag, 0);
EXPECT_TRUE(local_marking_bag.IsLocalEmpty());
EXPECT_TRUE(marking_bag.IsGlobalEmpty());
}
TEST(WorkStealingBag, LocalPushPop) {
WorkStealingBag marking_bag;
LocalWorkStealingBag local_marking_bag(&marking_bag, 0);
HeapObject dummy;
HeapObject* retrieved = nullptr;
EXPECT_TRUE(local_marking_bag.Push(&dummy));
EXPECT_FALSE(local_marking_bag.IsLocalEmpty());
EXPECT_TRUE(local_marking_bag.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
}
TEST(WorkStealingBag, LocalIsBasedOnId) {
WorkStealingBag marking_bag;
// Use the same id.
LocalWorkStealingBag local_marking_bag1(&marking_bag, 0);
LocalWorkStealingBag local_marking_bag2(&marking_bag, 0);
HeapObject dummy;
HeapObject* retrieved = nullptr;
EXPECT_TRUE(local_marking_bag1.Push(&dummy));
EXPECT_FALSE(local_marking_bag1.IsLocalEmpty());
EXPECT_FALSE(local_marking_bag2.IsLocalEmpty());
EXPECT_TRUE(local_marking_bag2.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_TRUE(local_marking_bag1.IsLocalEmpty());
EXPECT_TRUE(local_marking_bag2.IsLocalEmpty());
}
TEST(WorkStealingBag, LocalPushStaysPrivate) {
WorkStealingBag marking_bag;
LocalWorkStealingBag local_marking_bag1(&marking_bag, 0);
LocalWorkStealingBag local_marking_bag2(&marking_bag, 1);
HeapObject dummy;
HeapObject* retrieved = nullptr;
EXPECT_TRUE(marking_bag.IsGlobalEmpty());
EXPECT_TRUE(local_marking_bag1.Push(&dummy));
EXPECT_FALSE(marking_bag.IsGlobalEmpty());
EXPECT_FALSE(local_marking_bag2.Pop(&retrieved));
EXPECT_EQ(nullptr, retrieved);
EXPECT_TRUE(local_marking_bag1.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_TRUE(marking_bag.IsGlobalEmpty());
}
TEST(WorkStealingBag, SingleSegmentSteal) {
WorkStealingBag marking_bag;
LocalWorkStealingBag local_marking_bag1(&marking_bag, 0);
LocalWorkStealingBag local_marking_bag2(&marking_bag, 1);
HeapObject dummy;
for (size_t i = 0; i < WorkStealingBag::kSegmentCapacity; i++) {
EXPECT_TRUE(local_marking_bag1.Push(&dummy));
}
HeapObject* retrieved = nullptr;
// One more push/pop to publish the full segment.
EXPECT_TRUE(local_marking_bag1.Push(nullptr));
EXPECT_TRUE(local_marking_bag1.Pop(&retrieved));
EXPECT_EQ(nullptr, retrieved);
// Stealing.
for (size_t i = 0; i < WorkStealingBag::kSegmentCapacity; i++) {
EXPECT_TRUE(local_marking_bag2.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_FALSE(local_marking_bag1.Pop(&retrieved));
}
EXPECT_TRUE(marking_bag.IsGlobalEmpty());
}
TEST(WorkStealingBag, MultipleSegmentsStolen) {
WorkStealingBag marking_bag;
LocalWorkStealingBag local_marking_bag1(&marking_bag, 0);
LocalWorkStealingBag local_marking_bag2(&marking_bag, 1);
LocalWorkStealingBag local_marking_bag3(&marking_bag, 2);
HeapObject dummy1;
HeapObject dummy2;
for (size_t i = 0; i < WorkStealingBag::kSegmentCapacity; i++) {
EXPECT_TRUE(local_marking_bag1.Push(&dummy1));
}
for (size_t i = 0; i < WorkStealingBag::kSegmentCapacity; i++) {
EXPECT_TRUE(local_marking_bag1.Push(&dummy2));
}
HeapObject* retrieved = nullptr;
HeapObject dummy3;
// One more push/pop to publish the full segment.
EXPECT_TRUE(local_marking_bag1.Push(&dummy3));
EXPECT_TRUE(local_marking_bag1.Pop(&retrieved));
EXPECT_EQ(&dummy3, retrieved);
// Stealing.
EXPECT_TRUE(local_marking_bag2.Pop(&retrieved));
HeapObject* const expect_bag2 = retrieved;
EXPECT_TRUE(local_marking_bag3.Pop(&retrieved));
HeapObject* 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);
for (size_t i = 1; i < WorkStealingBag::kSegmentCapacity; i++) {
EXPECT_TRUE(local_marking_bag2.Pop(&retrieved));
EXPECT_EQ(expect_bag2, retrieved);
EXPECT_FALSE(local_marking_bag1.Pop(&retrieved));
}
for (size_t i = 1; i < WorkStealingBag::kSegmentCapacity; i++) {
EXPECT_TRUE(local_marking_bag3.Pop(&retrieved));
EXPECT_EQ(expect_bag3, retrieved);
EXPECT_FALSE(local_marking_bag1.Pop(&retrieved));
}
EXPECT_TRUE(marking_bag.IsGlobalEmpty());
}
} // namespace internal
} // namespace v8
// Copyright 2017 the V8 project 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 "src/heap/workstealing-marking-deque.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
class HeapObject {};
TEST(WorkStealingMarkingDeque, LocalEmpty) {
WorkStealingMarkingDeque marking_deque;
LocalWorkStealingMarkingDeque local_marking_deque(&marking_deque, 0);
EXPECT_TRUE(local_marking_deque.IsEmpty());
}
TEST(WorkStealingMarkingDeque, LocalPushPop) {
WorkStealingMarkingDeque marking_deque;
LocalWorkStealingMarkingDeque local_marking_deque(&marking_deque, 0);
HeapObject* object1 = new HeapObject();
HeapObject* object2 = nullptr;
EXPECT_TRUE(local_marking_deque.Push(object1));
EXPECT_FALSE(local_marking_deque.IsEmpty());
EXPECT_TRUE(local_marking_deque.Pop(&object2));
EXPECT_EQ(object1, object2);
delete object1;
}
} // namespace internal
} // namespace v8
......@@ -110,7 +110,7 @@
'heap/slot-set-unittest.cc',
'heap/spaces-unittest.cc',
'heap/unmapper-unittest.cc',
'heap/workstealing-marking-deque-unittest.cc',
'heap/workstealing-bag-unittest.cc',
'interpreter/bytecodes-unittest.cc',
'interpreter/bytecode-array-builder-unittest.cc',
'interpreter/bytecode-array-iterator-unittest.cc',
......
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