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

Reland "cppgc, heap: Don't eagerly allocate worklist segments"

This is a reland of c99147c6

Original change's description:
> cppgc, heap: Don't eagerly allocate worklist segments
>
> Bug: chromium:1056170
> Change-Id: I75a6b5f52bfe8dd71abc086e5d1e060759ad7fc0
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2391254
> Commit-Queue: Omer Katz <omerkatz@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#69778}

Bug: chromium:1056170
Change-Id: I4633da065976a6b2710d2f23b946fd2af0e65c83
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2401425Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69806}
parent 01dbc9f6
...@@ -4256,6 +4256,7 @@ v8_source_set("v8_cppgc_shared") { ...@@ -4256,6 +4256,7 @@ v8_source_set("v8_cppgc_shared") {
sources = [ sources = [
"src/heap/base/stack.cc", "src/heap/base/stack.cc",
"src/heap/base/stack.h", "src/heap/base/stack.h",
"src/heap/base/worklist.cc",
"src/heap/base/worklist.h", "src/heap/base/worklist.h",
] ]
......
// Copyright 2020 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/base/worklist.h"
namespace heap {
namespace base {
namespace internal {
SegmentBase SegmentBase::kSentinelSegment(0);
} // namespace internal
} // namespace base
} // namespace heap
...@@ -16,11 +16,29 @@ ...@@ -16,11 +16,29 @@
namespace heap { namespace heap {
namespace base { namespace base {
namespace internal {
class V8_EXPORT_PRIVATE SegmentBase {
public:
static SegmentBase kSentinelSegment;
explicit SegmentBase(uint16_t capacity) : capacity_(capacity) {}
size_t Size() const { return index_; }
bool IsEmpty() const { return index_ == 0; }
bool IsFull() const { return index_ == capacity_; }
void Clear() { index_ = 0; }
protected:
const uint16_t capacity_;
uint16_t index_ = 0;
};
} // namespace internal
// A global marking worklist that is similar the existing Worklist // A global marking worklist that is similar the existing Worklist
// but does not reserve space and keep track of the local segments. // but does not reserve space and keep track of the local segments.
// Eventually this will replace Worklist after all its current uses // Eventually this will replace Worklist after all its current uses
// are migrated. // are migrated.
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
class Worklist { class Worklist {
public: public:
static const int kSegmentSize = SegmentSize; static const int kSegmentSize = SegmentSize;
...@@ -61,34 +79,33 @@ class Worklist { ...@@ -61,34 +79,33 @@ class Worklist {
std::atomic<size_t> size_{0}; std::atomic<size_t> size_{0};
}; };
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Push(Segment* segment) { void Worklist<EntryType, SegmentSize>::Push(Segment* segment) {
DCHECK(!segment->IsEmpty());
v8::base::MutexGuard guard(&lock_); v8::base::MutexGuard guard(&lock_);
segment->set_next(top_); segment->set_next(top_);
set_top(segment); set_top(segment);
size_.fetch_add(1, std::memory_order_relaxed); size_.fetch_add(1, std::memory_order_relaxed);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Pop(Segment** segment) { bool Worklist<EntryType, SegmentSize>::Pop(Segment** segment) {
v8::base::MutexGuard guard(&lock_); v8::base::MutexGuard guard(&lock_);
if (top_ != nullptr) { if (top_ == nullptr) return false;
DCHECK_LT(0U, size_); DCHECK_LT(0U, size_);
size_.fetch_sub(1, std::memory_order_relaxed); size_.fetch_sub(1, std::memory_order_relaxed);
*segment = top_; *segment = top_;
set_top(top_->next()); set_top(top_->next());
return true; return true;
}
return false;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::IsEmpty() { bool Worklist<EntryType, SegmentSize>::IsEmpty() {
return v8::base::AsAtomicPtr(&top_)->load(std::memory_order_relaxed) == return v8::base::AsAtomicPtr(&top_)->load(std::memory_order_relaxed) ==
nullptr; nullptr;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
size_t Worklist<EntryType, SegmentSize>::Size() { size_t Worklist<EntryType, SegmentSize>::Size() {
// It is safe to read |size_| without a lock since this variable is // It is safe to read |size_| without a lock since this variable is
// atomic, keeping in mind that threads may not immediately see the new // atomic, keeping in mind that threads may not immediately see the new
...@@ -96,7 +113,7 @@ size_t Worklist<EntryType, SegmentSize>::Size() { ...@@ -96,7 +113,7 @@ size_t Worklist<EntryType, SegmentSize>::Size() {
return size_.load(std::memory_order_relaxed); return size_.load(std::memory_order_relaxed);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Clear() { void Worklist<EntryType, SegmentSize>::Clear() {
v8::base::MutexGuard guard(&lock_); v8::base::MutexGuard guard(&lock_);
size_.store(0, std::memory_order_relaxed); size_.store(0, std::memory_order_relaxed);
...@@ -109,7 +126,7 @@ void Worklist<EntryType, SegmentSize>::Clear() { ...@@ -109,7 +126,7 @@ void Worklist<EntryType, SegmentSize>::Clear() {
set_top(nullptr); set_top(nullptr);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
template <typename Callback> template <typename Callback>
void Worklist<EntryType, SegmentSize>::Update(Callback callback) { void Worklist<EntryType, SegmentSize>::Update(Callback callback) {
v8::base::MutexGuard guard(&lock_); v8::base::MutexGuard guard(&lock_);
...@@ -137,7 +154,7 @@ void Worklist<EntryType, SegmentSize>::Update(Callback callback) { ...@@ -137,7 +154,7 @@ void Worklist<EntryType, SegmentSize>::Update(Callback callback) {
size_.fetch_sub(num_deleted, std::memory_order_relaxed); size_.fetch_sub(num_deleted, std::memory_order_relaxed);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
template <typename Callback> template <typename Callback>
void Worklist<EntryType, SegmentSize>::Iterate(Callback callback) { void Worklist<EntryType, SegmentSize>::Iterate(Callback callback) {
v8::base::MutexGuard guard(&lock_); v8::base::MutexGuard guard(&lock_);
...@@ -146,7 +163,7 @@ void Worklist<EntryType, SegmentSize>::Iterate(Callback callback) { ...@@ -146,7 +163,7 @@ void Worklist<EntryType, SegmentSize>::Iterate(Callback callback) {
} }
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Merge( void Worklist<EntryType, SegmentSize>::Merge(
Worklist<EntryType, SegmentSize>* other) { Worklist<EntryType, SegmentSize>* other) {
Segment* top = nullptr; Segment* top = nullptr;
...@@ -173,19 +190,14 @@ void Worklist<EntryType, SegmentSize>::Merge( ...@@ -173,19 +190,14 @@ void Worklist<EntryType, SegmentSize>::Merge(
} }
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
class Worklist<EntryType, SegmentSize>::Segment { class Worklist<EntryType, SegmentSize>::Segment : public internal::SegmentBase {
public: public:
static const size_t kSize = SegmentSize; static const uint16_t kSize = SegmentSize;
Segment() = default;
bool Push(EntryType entry);
bool Pop(EntryType* entry);
size_t Size() const { return index_; } Segment() : internal::SegmentBase(kSize) {}
bool IsEmpty() const { return index_ == 0; } void Push(EntryType entry);
bool IsFull() const { return index_ == kSize; } void Pop(EntryType* entry);
void Clear() { index_ = 0; }
template <typename Callback> template <typename Callback>
void Update(Callback callback); void Update(Callback callback);
...@@ -197,25 +209,22 @@ class Worklist<EntryType, SegmentSize>::Segment { ...@@ -197,25 +209,22 @@ class Worklist<EntryType, SegmentSize>::Segment {
private: private:
Segment* next_ = nullptr; Segment* next_ = nullptr;
size_t index_ = 0;
EntryType entries_[kSize]; EntryType entries_[kSize];
}; };
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Segment::Push(EntryType entry) { void Worklist<EntryType, SegmentSize>::Segment::Push(EntryType entry) {
if (IsFull()) return false; DCHECK(!IsFull());
entries_[index_++] = entry; entries_[index_++] = entry;
return true;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Segment::Pop(EntryType* entry) { void Worklist<EntryType, SegmentSize>::Segment::Pop(EntryType* entry) {
if (IsEmpty()) return false; DCHECK(!IsEmpty());
*entry = entries_[--index_]; *entry = entries_[--index_];
return true;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
template <typename Callback> template <typename Callback>
void Worklist<EntryType, SegmentSize>::Segment::Update(Callback callback) { void Worklist<EntryType, SegmentSize>::Segment::Update(Callback callback) {
size_t new_index = 0; size_t new_index = 0;
...@@ -227,7 +236,7 @@ void Worklist<EntryType, SegmentSize>::Segment::Update(Callback callback) { ...@@ -227,7 +236,7 @@ void Worklist<EntryType, SegmentSize>::Segment::Update(Callback callback) {
index_ = new_index; index_ = new_index;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
template <typename Callback> template <typename Callback>
void Worklist<EntryType, SegmentSize>::Segment::Iterate( void Worklist<EntryType, SegmentSize>::Segment::Iterate(
Callback callback) const { Callback callback) const {
...@@ -237,7 +246,7 @@ void Worklist<EntryType, SegmentSize>::Segment::Iterate( ...@@ -237,7 +246,7 @@ void Worklist<EntryType, SegmentSize>::Segment::Iterate(
} }
// A thread-local view of the marking worklist. // A thread-local view of the marking worklist.
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
class Worklist<EntryType, SegmentSize>::Local { class Worklist<EntryType, SegmentSize>::Local {
public: public:
using ItemType = EntryType; using ItemType = EntryType;
...@@ -270,32 +279,55 @@ class Worklist<EntryType, SegmentSize>::Local { ...@@ -270,32 +279,55 @@ class Worklist<EntryType, SegmentSize>::Local {
void PublishPushSegment(); void PublishPushSegment();
void PublishPopSegment(); void PublishPopSegment();
bool StealPopSegment(); bool StealPopSegment();
Segment* NewSegment() const { Segment* NewSegment() const {
// Bottleneck for filtering in crash dumps. // Bottleneck for filtering in crash dumps.
return new Segment(); return new Segment();
} }
void DeleteSegment(internal::SegmentBase* segment) const {
if (segment == &internal::SegmentBase::kSentinelSegment) return;
delete static_cast<Segment*>(segment);
}
inline Segment* push_segment() {
DCHECK_NE(&internal::SegmentBase::kSentinelSegment, push_segment_);
return static_cast<Segment*>(push_segment_);
}
inline const Segment* push_segment() const {
DCHECK_NE(&internal::SegmentBase::kSentinelSegment, push_segment_);
return static_cast<const Segment*>(push_segment_);
}
inline Segment* pop_segment() {
DCHECK_NE(&internal::SegmentBase::kSentinelSegment, pop_segment_);
return static_cast<Segment*>(pop_segment_);
}
inline const Segment* pop_segment() const {
DCHECK_NE(&internal::SegmentBase::kSentinelSegment, pop_segment_);
return static_cast<const Segment*>(pop_segment_);
}
Worklist<EntryType, SegmentSize>* worklist_ = nullptr; Worklist<EntryType, SegmentSize>* worklist_ = nullptr;
Segment* push_segment_ = nullptr; internal::SegmentBase* push_segment_ = nullptr;
Segment* pop_segment_ = nullptr; internal::SegmentBase* pop_segment_ = nullptr;
}; };
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
Worklist<EntryType, SegmentSize>::Local::Local( Worklist<EntryType, SegmentSize>::Local::Local(
Worklist<EntryType, SegmentSize>* worklist) Worklist<EntryType, SegmentSize>* worklist)
: worklist_(worklist), : worklist_(worklist),
push_segment_(NewSegment()), push_segment_(&internal::SegmentBase::kSentinelSegment),
pop_segment_(NewSegment()) {} pop_segment_(&internal::SegmentBase::kSentinelSegment) {}
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
Worklist<EntryType, SegmentSize>::Local::~Local() { Worklist<EntryType, SegmentSize>::Local::~Local() {
CHECK_IMPLIES(push_segment_, push_segment_->IsEmpty()); CHECK_IMPLIES(push_segment_, push_segment_->IsEmpty());
CHECK_IMPLIES(pop_segment_, pop_segment_->IsEmpty()); CHECK_IMPLIES(pop_segment_, pop_segment_->IsEmpty());
delete push_segment_; DeleteSegment(push_segment_);
delete pop_segment_; DeleteSegment(pop_segment_);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
Worklist<EntryType, SegmentSize>::Local::Local( Worklist<EntryType, SegmentSize>::Local::Local(
Worklist<EntryType, SegmentSize>::Local&& other) V8_NOEXCEPT { Worklist<EntryType, SegmentSize>::Local&& other) V8_NOEXCEPT {
worklist_ = other.worklist_; worklist_ = other.worklist_;
...@@ -306,7 +338,7 @@ Worklist<EntryType, SegmentSize>::Local::Local( ...@@ -306,7 +338,7 @@ Worklist<EntryType, SegmentSize>::Local::Local(
other.pop_segment_ = nullptr; other.pop_segment_ = nullptr;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
typename Worklist<EntryType, SegmentSize>::Local& typename Worklist<EntryType, SegmentSize>::Local&
Worklist<EntryType, SegmentSize>::Local::operator=( Worklist<EntryType, SegmentSize>::Local::operator=(
Worklist<EntryType, SegmentSize>::Local&& other) V8_NOEXCEPT { Worklist<EntryType, SegmentSize>::Local&& other) V8_NOEXCEPT {
...@@ -324,81 +356,75 @@ Worklist<EntryType, SegmentSize>::Local::operator=( ...@@ -324,81 +356,75 @@ Worklist<EntryType, SegmentSize>::Local::operator=(
return *this; return *this;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Local::Push(EntryType entry) { void Worklist<EntryType, SegmentSize>::Local::Push(EntryType entry) {
if (V8_UNLIKELY(!push_segment_->Push(entry))) { if (V8_UNLIKELY(push_segment_->IsFull())) {
PublishPushSegment(); PublishPushSegment();
bool success = push_segment_->Push(entry);
USE(success);
DCHECK(success);
} }
push_segment()->Push(entry);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Local::Pop(EntryType* entry) { bool Worklist<EntryType, SegmentSize>::Local::Pop(EntryType* entry) {
if (!pop_segment_->Pop(entry)) { if (pop_segment_->IsEmpty()) {
if (!push_segment_->IsEmpty()) { if (!push_segment_->IsEmpty()) {
std::swap(push_segment_, pop_segment_); std::swap(push_segment_, pop_segment_);
} else if (!StealPopSegment()) { } else if (!StealPopSegment()) {
return false; return false;
} }
bool success = pop_segment_->Pop(entry);
USE(success);
DCHECK(success);
} }
pop_segment()->Pop(entry);
return true; return true;
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Local::IsLocalAndGlobalEmpty() const { bool Worklist<EntryType, SegmentSize>::Local::IsLocalAndGlobalEmpty() const {
return IsLocalEmpty() && IsGlobalEmpty(); return IsLocalEmpty() && IsGlobalEmpty();
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Local::IsLocalEmpty() const { bool Worklist<EntryType, SegmentSize>::Local::IsLocalEmpty() const {
return push_segment_->IsEmpty() && pop_segment_->IsEmpty(); return push_segment_->IsEmpty() && pop_segment_->IsEmpty();
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Local::IsGlobalEmpty() const { bool Worklist<EntryType, SegmentSize>::Local::IsGlobalEmpty() const {
return worklist_->IsEmpty(); return worklist_->IsEmpty();
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Local::Publish() { void Worklist<EntryType, SegmentSize>::Local::Publish() {
if (!push_segment_->IsEmpty()) { if (!push_segment_->IsEmpty()) PublishPushSegment();
PublishPushSegment(); if (!pop_segment_->IsEmpty()) PublishPopSegment();
}
if (!pop_segment_->IsEmpty()) {
PublishPopSegment();
}
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Local::Merge( void Worklist<EntryType, SegmentSize>::Local::Merge(
Worklist<EntryType, SegmentSize>::Local* other) { Worklist<EntryType, SegmentSize>::Local* other) {
other->Publish(); other->Publish();
worklist_->Merge(other->worklist_); worklist_->Merge(other->worklist_);
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Local::PublishPushSegment() { void Worklist<EntryType, SegmentSize>::Local::PublishPushSegment() {
worklist_->Push(push_segment_); if (push_segment_ != &internal::SegmentBase::kSentinelSegment)
worklist_->Push(push_segment());
push_segment_ = NewSegment(); push_segment_ = NewSegment();
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Local::PublishPopSegment() { void Worklist<EntryType, SegmentSize>::Local::PublishPopSegment() {
worklist_->Push(pop_segment_); if (pop_segment_ != &internal::SegmentBase::kSentinelSegment)
worklist_->Push(pop_segment());
pop_segment_ = NewSegment(); pop_segment_ = NewSegment();
} }
template <typename EntryType, int SegmentSize> template <typename EntryType, uint16_t SegmentSize>
bool Worklist<EntryType, SegmentSize>::Local::StealPopSegment() { bool Worklist<EntryType, SegmentSize>::Local::StealPopSegment() {
if (worklist_->IsEmpty()) return false; if (worklist_->IsEmpty()) return false;
Segment* new_segment = nullptr; Segment* new_segment = nullptr;
if (worklist_->Pop(&new_segment)) { if (worklist_->Pop(&new_segment)) {
delete pop_segment_; DeleteSegment(pop_segment_);
pop_segment_ = new_segment; pop_segment_ = new_segment;
return true; return true;
} }
......
...@@ -69,6 +69,7 @@ v8_executable("cppgc_unittests") { ...@@ -69,6 +69,7 @@ v8_executable("cppgc_unittests") {
deps = [ deps = [
":cppgc_unittests_sources", ":cppgc_unittests_sources",
":v8_cppgc_shared_unittests_sources",
"../..:cppgc_for_testing", "../..:cppgc_for_testing",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
...@@ -136,6 +137,7 @@ v8_executable("unittests") { ...@@ -136,6 +137,7 @@ v8_executable("unittests") {
deps = [ deps = [
":cppgc_unittests_sources", ":cppgc_unittests_sources",
":unittests_sources", ":unittests_sources",
":v8_cppgc_shared_unittests_sources",
"../..:v8_for_testing", "../..:v8_for_testing",
"../..:v8_libbase", "../..:v8_libbase",
"../..:v8_libplatform", "../..:v8_libplatform",
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#include "src/heap/base/worklist.h" #include "src/heap/base/worklist.h"
#include "test/unittests/heap/cppgc/tests.h" #include "testing/gtest/include/gtest/gtest.h"
namespace heap { namespace heap {
namespace base { namespace base {
...@@ -23,17 +23,17 @@ TEST(CppgcWorkListTest, SegmentCreate) { ...@@ -23,17 +23,17 @@ TEST(CppgcWorkListTest, SegmentCreate) {
TEST(CppgcWorkListTest, SegmentPush) { TEST(CppgcWorkListTest, SegmentPush) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_EQ(0u, segment.Size()); EXPECT_EQ(0u, segment.Size());
EXPECT_TRUE(segment.Push(nullptr)); segment.Push(nullptr);
EXPECT_EQ(1u, segment.Size()); EXPECT_EQ(1u, segment.Size());
} }
TEST(CppgcWorkListTest, SegmentPushPop) { TEST(CppgcWorkListTest, SegmentPushPop) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.Push(nullptr)); segment.Push(nullptr);
EXPECT_EQ(1u, segment.Size()); EXPECT_EQ(1u, segment.Size());
SomeObject dummy; SomeObject dummy;
SomeObject* object = &dummy; SomeObject* object = &dummy;
EXPECT_TRUE(segment.Pop(&object)); segment.Pop(&object);
EXPECT_EQ(0u, segment.Size()); EXPECT_EQ(0u, segment.Size());
EXPECT_EQ(nullptr, object); EXPECT_EQ(nullptr, object);
} }
...@@ -41,7 +41,7 @@ TEST(CppgcWorkListTest, SegmentPushPop) { ...@@ -41,7 +41,7 @@ TEST(CppgcWorkListTest, SegmentPushPop) {
TEST(CppgcWorkListTest, SegmentIsEmpty) { TEST(CppgcWorkListTest, SegmentIsEmpty) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
EXPECT_TRUE(segment.Push(nullptr)); segment.Push(nullptr);
EXPECT_FALSE(segment.IsEmpty()); EXPECT_FALSE(segment.IsEmpty());
} }
...@@ -49,44 +49,27 @@ TEST(CppgcWorkListTest, SegmentIsFull) { ...@@ -49,44 +49,27 @@ TEST(CppgcWorkListTest, SegmentIsFull) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_FALSE(segment.IsFull()); EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) { for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) {
EXPECT_TRUE(segment.Push(nullptr)); segment.Push(nullptr);
} }
EXPECT_TRUE(segment.IsFull()); EXPECT_TRUE(segment.IsFull());
} }
TEST(CppgcWorkListTest, SegmentClear) { TEST(CppgcWorkListTest, SegmentClear) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
EXPECT_TRUE(segment.Push(nullptr)); segment.Push(nullptr);
EXPECT_FALSE(segment.IsEmpty()); EXPECT_FALSE(segment.IsEmpty());
segment.Clear(); segment.Clear();
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) { for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) {
EXPECT_TRUE(segment.Push(nullptr)); segment.Push(nullptr);
} }
} }
TEST(CppgcWorkListTest, SegmentFullPushFails) {
TestWorklist::Segment segment;
EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) {
EXPECT_TRUE(segment.Push(nullptr));
}
EXPECT_TRUE(segment.IsFull());
EXPECT_FALSE(segment.Push(nullptr));
}
TEST(CppgcWorkListTest, SegmentEmptyPopFails) {
TestWorklist::Segment segment;
EXPECT_TRUE(segment.IsEmpty());
SomeObject* object;
EXPECT_FALSE(segment.Pop(&object));
}
TEST(CppgcWorkListTest, SegmentUpdateFalse) { TEST(CppgcWorkListTest, SegmentUpdateFalse) {
TestWorklist::Segment segment; TestWorklist::Segment segment;
SomeObject* object; SomeObject* object;
object = reinterpret_cast<SomeObject*>(&object); object = reinterpret_cast<SomeObject*>(&object);
EXPECT_TRUE(segment.Push(object)); segment.Push(object);
segment.Update([](SomeObject* object, SomeObject** out) { return false; }); segment.Update([](SomeObject* object, SomeObject** out) { return false; });
EXPECT_TRUE(segment.IsEmpty()); EXPECT_TRUE(segment.IsEmpty());
} }
...@@ -97,13 +80,13 @@ TEST(CppgcWorkListTest, SegmentUpdate) { ...@@ -97,13 +80,13 @@ TEST(CppgcWorkListTest, SegmentUpdate) {
objectA = reinterpret_cast<SomeObject*>(&objectA); objectA = reinterpret_cast<SomeObject*>(&objectA);
SomeObject* objectB; SomeObject* objectB;
objectB = reinterpret_cast<SomeObject*>(&objectB); objectB = reinterpret_cast<SomeObject*>(&objectB);
EXPECT_TRUE(segment.Push(objectA)); segment.Push(objectA);
segment.Update([objectB](SomeObject* object, SomeObject** out) { segment.Update([objectB](SomeObject* object, SomeObject** out) {
*out = objectB; *out = objectB;
return true; return true;
}); });
SomeObject* object; SomeObject* object;
EXPECT_TRUE(segment.Pop(&object)); segment.Pop(&object);
EXPECT_EQ(object, objectB); EXPECT_EQ(object, objectB);
} }
...@@ -324,5 +307,26 @@ TEST(CppgcWorkListTest, MergeGlobalPool) { ...@@ -324,5 +307,26 @@ TEST(CppgcWorkListTest, MergeGlobalPool) {
EXPECT_TRUE(worklist2.IsEmpty()); EXPECT_TRUE(worklist2.IsEmpty());
} }
#ifdef DEBUG
TEST(CppgcWorkListDeathTest, DiesOnPushToFullSegment) {
TestWorklist::Segment segment;
EXPECT_FALSE(segment.IsFull());
for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) {
segment.Push(nullptr);
}
EXPECT_TRUE(segment.IsFull());
EXPECT_DEATH_IF_SUPPORTED(segment.Push(nullptr), "");
}
TEST(CppgcWorkListDeathTest, DiesOnPopFromEmptySegment) {
TestWorklist::Segment segment;
EXPECT_TRUE(segment.IsEmpty());
SomeObject* object;
EXPECT_DEATH_IF_SUPPORTED(segment.Pop(&object), "");
}
#endif // DEBUG
} // namespace base } // namespace base
} // namespace heap } // namespace heap
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