diff --git a/src/base/platform/platform-win32.cc b/src/base/platform/platform-win32.cc index 9ce6cc756f..89595dbed9 100644 --- a/src/base/platform/platform-win32.cc +++ b/src/base/platform/platform-win32.cc @@ -19,7 +19,7 @@ // This has to come after windows.h. #include -#include // For SymLoadModule64 and al. +#include // For SymLoadModule64 and al. #include // For timeGetTime(). #include // For Module32First and al. diff --git a/src/heap/base/worklist.cc b/src/heap/base/worklist.cc index bda562ff5b..effca7fbc7 100644 --- a/src/heap/base/worklist.cc +++ b/src/heap/base/worklist.cc @@ -4,16 +4,12 @@ #include "src/heap/base/worklist.h" -namespace heap { -namespace base { -namespace internal { +namespace heap::base::internal { // static SegmentBase* SegmentBase::GetSentinelSegmentAddress() { -static SegmentBase kSentinelSegment(0); -return &kSentinelSegment; + static SegmentBase sentinel_segment(0); + return &sentinel_segment; } -} // namespace internal -} // namespace base -} // namespace heap +} // namespace heap::base::internal diff --git a/src/heap/base/worklist.h b/src/heap/base/worklist.h index c5c5c5f404..ef916b7eb3 100644 --- a/src/heap/base/worklist.h +++ b/src/heap/base/worklist.h @@ -11,19 +11,18 @@ #include "src/base/atomic-utils.h" #include "src/base/logging.h" #include "src/base/platform/mutex.h" -#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck - -namespace heap { -namespace base { +namespace heap::base { namespace internal { + class V8_EXPORT_PRIVATE SegmentBase { public: static SegmentBase* GetSentinelSegmentAddress(); - explicit SegmentBase(uint16_t capacity) : capacity_(capacity) {} + explicit constexpr SegmentBase(uint16_t capacity) : capacity_(capacity) {} size_t Size() const { return index_; } + size_t Capacity() const { return capacity_; } bool IsEmpty() const { return index_ == 0; } bool IsFull() const { return index_ == capacity_; } void Clear() { index_ = 0; } @@ -34,104 +33,114 @@ class V8_EXPORT_PRIVATE SegmentBase { }; } // namespace internal -// A global marking worklist that is similar the existing Worklist -// but does not reserve space and keep track of the local segments. -// Eventually this will replace Worklist after all its current uses -// are migrated. -template -class Worklist { +// A global worklist based on segments which allows for a thread-local +// producer/consumer pattern with global work stealing. +// +// - Entries in the worklist are of type `EntryType`. +// - Segments have a capacity of at least `MinSegmentSize` but possibly more. +// +// All methods on the worklist itself only consider the list of segments. +// Unpublished work in local views is not visible. +template +class Worklist final { public: - static const int kSegmentSize = SegmentSize; - class Segment; + // A thread-local view on the worklist. Any work that is not published from + // the local view is not visible to the global worklist. class Local; + class Segment; + + static constexpr int kMinSegmentSizeForTesting = MinSegmentSize; Worklist() = default; ~Worklist() { CHECK(IsEmpty()); } + Worklist(const Worklist&) = delete; + Worklist& operator=(const Worklist&) = delete; + + // Returns true if the global worklist is empty and false otherwise. May be + // read concurrently for an approximation. + bool IsEmpty() const; + // Returns the number of segments in the global worklist. May be read + // concurrently for an approximation. + size_t Size() const; + + // Moves the segments from `other` into this worklist. + void Merge(Worklist* other); + + // Swaps the segments with `other`. + void Swap(Worklist* other); + + // Removes all segments from the worklist. + void Clear(); + + // Invokes `callback` on each item. Callback is of type `bool(EntryType&)` and + // should return true if the entry should be kept and false if the entry + // should be removed. + template + void Update(Callback callback); + + // Invokes `callback` on each item. Callback is of type `void(EntryType&)`. + template + void Iterate(Callback callback) const; + + private: void Push(Segment* segment); bool Pop(Segment** segment); - // Returns true if the list of segments is empty. - bool IsEmpty() const; - // Returns the number of segments in the list. - size_t Size() const; - - // Moves the segments of the given marking worklist into this - // marking worklist. - void Merge(Worklist* other); - - // Swaps the segments with the given marking worklist. - void Swap(Worklist* other); - - // These functions are not thread-safe. They should be called only - // if all local marking worklists that use the current worklist have - // been published and are empty. - void Clear(); - template - void Update(Callback callback); - template - void Iterate(Callback callback); - - private: - void set_top(Segment* segment) { - v8::base::AsAtomicPtr(&top_)->store(segment, std::memory_order_relaxed); - } - - v8::base::Mutex lock_; + mutable v8::base::Mutex lock_; Segment* top_ = nullptr; std::atomic size_{0}; }; -template -void Worklist::Push(Segment* segment) { +template +void Worklist::Push(Segment* segment) { DCHECK(!segment->IsEmpty()); v8::base::MutexGuard guard(&lock_); segment->set_next(top_); - set_top(segment); + top_ = segment; size_.fetch_add(1, std::memory_order_relaxed); } -template -bool Worklist::Pop(Segment** segment) { +template +bool Worklist::Pop(Segment** segment) { v8::base::MutexGuard guard(&lock_); if (top_ == nullptr) return false; DCHECK_LT(0U, size_); size_.fetch_sub(1, std::memory_order_relaxed); *segment = top_; - set_top(top_->next()); + top_ = top_->next(); return true; } -template -bool Worklist::IsEmpty() const { - return v8::base::AsAtomicPtr(&top_)->load(std::memory_order_relaxed) == - nullptr; +template +bool Worklist::IsEmpty() const { + return Size() == 0; } -template -size_t Worklist::Size() const { +template +size_t Worklist::Size() const { // 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 // value when it is updated. return size_.load(std::memory_order_relaxed); } -template -void Worklist::Clear() { +template +void Worklist::Clear() { v8::base::MutexGuard guard(&lock_); size_.store(0, std::memory_order_relaxed); Segment* current = top_; while (current != nullptr) { Segment* tmp = current; current = current->next(); - delete tmp; + Segment::Delete(tmp); } - set_top(nullptr); + top_ = nullptr; } -template +template template -void Worklist::Update(Callback callback) { +void Worklist::Update(Callback callback) { v8::base::MutexGuard guard(&lock_); Segment* prev = nullptr; Segment* current = top_; @@ -148,7 +157,7 @@ void Worklist::Update(Callback callback) { } Segment* tmp = current; current = current->next(); - delete tmp; + Segment::Delete(tmp); } else { prev = current; current = current->next(); @@ -157,18 +166,18 @@ void Worklist::Update(Callback callback) { size_.fetch_sub(num_deleted, std::memory_order_relaxed); } -template +template template -void Worklist::Iterate(Callback callback) { +void Worklist::Iterate(Callback callback) const { v8::base::MutexGuard guard(&lock_); for (Segment* current = top_; current != nullptr; current = current->next()) { current->Iterate(callback); } } -template -void Worklist::Merge( - Worklist* other) { +template +void Worklist::Merge( + Worklist* other) { Segment* top = nullptr; size_t other_size = 0; { @@ -177,7 +186,7 @@ void Worklist::Merge( top = other->top_; other_size = other->size_.load(std::memory_order_relaxed); other->size_.store(0, std::memory_order_relaxed); - other->set_top(nullptr); + other->top_ = nullptr; } // It's safe to iterate through these segments because the top was @@ -189,30 +198,38 @@ void Worklist::Merge( v8::base::MutexGuard guard(&lock_); size_.fetch_add(other_size, std::memory_order_relaxed); end->set_next(top_); - set_top(top); + top_ = top; } } -template -void Worklist::Swap( - Worklist* other) { +template +void Worklist::Swap( + Worklist* other) { v8::base::MutexGuard guard1(&lock_); v8::base::MutexGuard guard2(&other->lock_); Segment* top = top_; - set_top(other->top_); - other->set_top(top); + top_ = other->top_; + other->top_ = top; size_t other_size = other->size_.exchange( size_.load(std::memory_order_relaxed), std::memory_order_relaxed); size_.store(other_size, std::memory_order_relaxed); } -template -class Worklist::Segment : public internal::SegmentBase { +template +class Worklist::Segment final + : public internal::SegmentBase { public: - static const uint16_t kSize = SegmentSize; + static Segment* Create(uint16_t min_segment_size) { + // TODO(v8:13193): Refer to a cross-platform `malloc_usable_size()` to make + // use of all the memory allocated by `malloc()`. + void* memory = malloc(MallocSizeForCapacity(min_segment_size)); + return new (memory) Segment(min_segment_size); + } - void Push(EntryType entry); - void Pop(EntryType* entry); + static void Delete(Segment* segment) { free(segment); } + + V8_INLINE void Push(EntryType entry); + V8_INLINE void Pop(EntryType* entry); template void Update(Callback callback); @@ -223,89 +240,90 @@ class Worklist::Segment : public internal::SegmentBase { void set_next(Segment* segment) { next_ = segment; } private: - Segment() : internal::SegmentBase(kSize) {} + static constexpr size_t MallocSizeForCapacity(size_t num_entries) { + return sizeof(Segment) + sizeof(EntryType) * num_entries; + } + + constexpr explicit Segment(size_t capacity) + : internal::SegmentBase(capacity) {} + + EntryType& entry(size_t index) { + return reinterpret_cast(this + 1)[index]; + } + const EntryType& entry(size_t index) const { + return reinterpret_cast(this + 1)[index]; + } Segment* next_ = nullptr; - EntryType entries_[kSize]; - - friend class Worklist::Local; - - 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, SegmentUpdateFalse); - FRIEND_TEST(WorkListTest, SegmentUpdate); }; -template -void Worklist::Segment::Push(EntryType entry) { +template +void Worklist::Segment::Push(EntryType e) { DCHECK(!IsFull()); - entries_[index_++] = entry; + entry(index_++) = e; } -template -void Worklist::Segment::Pop(EntryType* entry) { +template +void Worklist::Segment::Pop(EntryType* e) { DCHECK(!IsEmpty()); - *entry = entries_[--index_]; + *e = entry(--index_); } -template +template template -void Worklist::Segment::Update(Callback callback) { +void Worklist::Segment::Update(Callback callback) { size_t new_index = 0; for (size_t i = 0; i < index_; i++) { - if (callback(entries_[i], &entries_[new_index])) { + if (callback(entry(i), &entry(new_index))) { new_index++; } } index_ = new_index; } -template +template template -void Worklist::Segment::Iterate( +void Worklist::Segment::Iterate( Callback callback) const { for (size_t i = 0; i < index_; i++) { - callback(entries_[i]); + callback(entry(i)); } } -// A thread-local view of the marking worklist. -template -class Worklist::Local { +// A thread-local on a given worklist. +template +class Worklist::Local final { public: using ItemType = EntryType; + // An empty local view does not have any segments and is not attached to a + // worklist. As such it will crash on any operation until it is initialized + // properly via move constructor. Local() = default; - explicit Local(Worklist* worklist); + explicit Local(Worklist* worklist); ~Local(); Local(Local&&) V8_NOEXCEPT; Local& operator=(Local&&) V8_NOEXCEPT; - // Disable copying since having multiple copies of the same - // local marking worklist is unsafe. + // Having multiple copies of the same local view may be unsafe. Local(const Local&) = delete; Local& operator=(const Local& other) = delete; - void Push(EntryType entry); - bool Pop(EntryType* entry); + V8_INLINE void Push(EntryType entry); + V8_INLINE bool Pop(EntryType* entry); bool IsLocalAndGlobalEmpty() const; bool IsLocalEmpty() const; bool IsGlobalEmpty() const; - void Publish(); - void Merge(Worklist::Local* other); - - bool IsEmpty() const; - void Clear(); - size_t PushSegmentSize() const { return push_segment_->Size(); } + void Publish(); + void Merge(Worklist::Local* other); + + void Clear(); + private: void PublishPushSegment(); void PublishPopSegment(); @@ -313,11 +331,11 @@ class Worklist::Local { Segment* NewSegment() const { // Bottleneck for filtering in crash dumps. - return new Segment(); + return Segment::Create(MinSegmentSize); } void DeleteSegment(internal::SegmentBase* segment) const { if (segment == internal::SegmentBase::GetSentinelSegmentAddress()) return; - delete static_cast(segment); + Segment::Delete(static_cast(segment)); } inline Segment* push_segment() { @@ -340,29 +358,29 @@ class Worklist::Local { return static_cast(pop_segment_); } - Worklist* worklist_ = nullptr; + Worklist* worklist_ = nullptr; internal::SegmentBase* push_segment_ = nullptr; internal::SegmentBase* pop_segment_ = nullptr; }; -template -Worklist::Local::Local( - Worklist* worklist) +template +Worklist::Local::Local( + Worklist* worklist) : worklist_(worklist), push_segment_(internal::SegmentBase::GetSentinelSegmentAddress()), pop_segment_(internal::SegmentBase::GetSentinelSegmentAddress()) {} -template -Worklist::Local::~Local() { +template +Worklist::Local::~Local() { CHECK_IMPLIES(push_segment_, push_segment_->IsEmpty()); CHECK_IMPLIES(pop_segment_, pop_segment_->IsEmpty()); DeleteSegment(push_segment_); DeleteSegment(pop_segment_); } -template -Worklist::Local::Local( - Worklist::Local&& other) V8_NOEXCEPT { +template +Worklist::Local::Local( + Worklist::Local&& other) V8_NOEXCEPT { worklist_ = other.worklist_; push_segment_ = other.push_segment_; pop_segment_ = other.pop_segment_; @@ -371,10 +389,10 @@ Worklist::Local::Local( other.pop_segment_ = nullptr; } -template -typename Worklist::Local& -Worklist::Local::operator=( - Worklist::Local&& other) V8_NOEXCEPT { +template +typename Worklist::Local& +Worklist::Local::operator=( + Worklist::Local&& other) V8_NOEXCEPT { if (this != &other) { DCHECK_NULL(worklist_); DCHECK_NULL(push_segment_); @@ -389,16 +407,16 @@ Worklist::Local::operator=( return *this; } -template -void Worklist::Local::Push(EntryType entry) { +template +void Worklist::Local::Push(EntryType entry) { if (V8_UNLIKELY(push_segment_->IsFull())) { PublishPushSegment(); } push_segment()->Push(entry); } -template -bool Worklist::Local::Pop(EntryType* entry) { +template +bool Worklist::Local::Pop(EntryType* entry) { if (pop_segment_->IsEmpty()) { if (!push_segment_->IsEmpty()) { std::swap(push_segment_, pop_segment_); @@ -410,50 +428,50 @@ bool Worklist::Local::Pop(EntryType* entry) { return true; } -template -bool Worklist::Local::IsLocalAndGlobalEmpty() const { +template +bool Worklist::Local::IsLocalAndGlobalEmpty() const { return IsLocalEmpty() && IsGlobalEmpty(); } -template -bool Worklist::Local::IsLocalEmpty() const { +template +bool Worklist::Local::IsLocalEmpty() const { return push_segment_->IsEmpty() && pop_segment_->IsEmpty(); } -template -bool Worklist::Local::IsGlobalEmpty() const { +template +bool Worklist::Local::IsGlobalEmpty() const { return worklist_->IsEmpty(); } -template -void Worklist::Local::Publish() { +template +void Worklist::Local::Publish() { if (!push_segment_->IsEmpty()) PublishPushSegment(); if (!pop_segment_->IsEmpty()) PublishPopSegment(); } -template -void Worklist::Local::Merge( - Worklist::Local* other) { +template +void Worklist::Local::Merge( + Worklist::Local* other) { other->Publish(); worklist_->Merge(other->worklist_); } -template -void Worklist::Local::PublishPushSegment() { +template +void Worklist::Local::PublishPushSegment() { if (push_segment_ != internal::SegmentBase::GetSentinelSegmentAddress()) worklist_->Push(push_segment()); push_segment_ = NewSegment(); } -template -void Worklist::Local::PublishPopSegment() { +template +void Worklist::Local::PublishPopSegment() { if (pop_segment_ != internal::SegmentBase::GetSentinelSegmentAddress()) worklist_->Push(pop_segment()); pop_segment_ = NewSegment(); } -template -bool Worklist::Local::StealPopSegment() { +template +bool Worklist::Local::StealPopSegment() { if (worklist_->IsEmpty()) return false; Segment* new_segment = nullptr; if (worklist_->Pop(&new_segment)) { @@ -464,18 +482,12 @@ bool Worklist::Local::StealPopSegment() { return false; } -template -bool Worklist::Local::IsEmpty() const { - return push_segment_->IsEmpty() && pop_segment_->IsEmpty(); -} - -template -void Worklist::Local::Clear() { +template +void Worklist::Local::Clear() { push_segment_->Clear(); pop_segment_->Clear(); } -} // namespace base -} // namespace heap +} // namespace heap::base #endif // V8_HEAP_BASE_WORKLIST_H_ diff --git a/src/heap/scavenger.cc b/src/heap/scavenger.cc index d611444c1a..f36ada0688 100644 --- a/src/heap/scavenger.cc +++ b/src/heap/scavenger.cc @@ -704,7 +704,7 @@ void Scavenger::Process(JobDelegate* delegate) { scavenge_visitor.Visit(object_and_size.first); done = false; if (delegate && ((++objects % kInterruptThreshold) == 0)) { - if (!copied_list_local_.IsEmpty()) { + if (!copied_list_local_.IsLocalEmpty()) { delegate->NotifyConcurrencyIncrease(); } } diff --git a/test/cctest/heap/test-concurrent-marking.cc b/test/cctest/heap/test-concurrent-marking.cc index 9f6087ee98..6c8189f43f 100644 --- a/test/cctest/heap/test-concurrent-marking.cc +++ b/test/cctest/heap/test-concurrent-marking.cc @@ -20,10 +20,10 @@ namespace heap { void PublishSegment(MarkingWorklist* worklist, HeapObject object) { MarkingWorklist::Local local(worklist); - for (size_t i = 0; i <= MarkingWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < MarkingWorklist::kMinSegmentSizeForTesting; i++) { local.Push(object); } - CHECK(local.Pop(&object)); + local.Publish(); } TEST(ConcurrentMarking) { diff --git a/test/unittests/heap/base/worklist-unittest.cc b/test/unittests/heap/base/worklist-unittest.cc index a52dcd3feb..5550fb4651 100644 --- a/test/unittests/heap/base/worklist-unittest.cc +++ b/test/unittests/heap/base/worklist-unittest.cc @@ -11,82 +11,90 @@ namespace base { class SomeObject {}; -using TestWorklist = Worklist; +constexpr size_t kMinSegmentSize = 64; +using TestWorklist = Worklist; +using Segment = TestWorklist::Segment; + +auto CreateTemporarySegment(size_t min_segment_size) { + return std::unique_ptr( + Segment::Create(min_segment_size), + [](Segment* s) { Segment::Delete(s); }); +} TEST(WorkListTest, SegmentCreate) { - TestWorklist::Segment segment; - EXPECT_TRUE(segment.IsEmpty()); - EXPECT_EQ(0u, segment.Size()); - EXPECT_FALSE(segment.IsFull()); + auto segment = CreateTemporarySegment(kMinSegmentSize); + EXPECT_TRUE(segment->IsEmpty()); + EXPECT_EQ(0u, segment->Size()); + EXPECT_FALSE(segment->IsFull()); } TEST(WorkListTest, SegmentPush) { - TestWorklist::Segment segment; - EXPECT_EQ(0u, segment.Size()); - segment.Push(nullptr); - EXPECT_EQ(1u, segment.Size()); + auto segment = CreateTemporarySegment(kMinSegmentSize); + EXPECT_EQ(0u, segment->Size()); + segment->Push(nullptr); + EXPECT_EQ(1u, segment->Size()); } TEST(WorkListTest, SegmentPushPop) { - TestWorklist::Segment segment; - segment.Push(nullptr); - EXPECT_EQ(1u, segment.Size()); + auto segment = CreateTemporarySegment(kMinSegmentSize); + segment->Push(nullptr); + EXPECT_EQ(1u, segment->Size()); SomeObject dummy; SomeObject* object = &dummy; - segment.Pop(&object); - EXPECT_EQ(0u, segment.Size()); + segment->Pop(&object); + EXPECT_EQ(0u, segment->Size()); EXPECT_EQ(nullptr, object); } TEST(WorkListTest, SegmentIsEmpty) { - TestWorklist::Segment segment; - EXPECT_TRUE(segment.IsEmpty()); - segment.Push(nullptr); - EXPECT_FALSE(segment.IsEmpty()); + auto segment = CreateTemporarySegment(kMinSegmentSize); + EXPECT_TRUE(segment->IsEmpty()); + segment->Push(nullptr); + EXPECT_FALSE(segment->IsEmpty()); } TEST(WorkListTest, SegmentIsFull) { - TestWorklist::Segment segment; - EXPECT_FALSE(segment.IsFull()); - for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) { - segment.Push(nullptr); + auto segment = CreateTemporarySegment(kMinSegmentSize); + EXPECT_FALSE(segment->IsFull()); + for (size_t i = 0; i < segment->Capacity(); i++) { + segment->Push(nullptr); } - EXPECT_TRUE(segment.IsFull()); + EXPECT_TRUE(segment->IsFull()); } TEST(WorkListTest, SegmentClear) { - TestWorklist::Segment segment; - segment.Push(nullptr); - EXPECT_FALSE(segment.IsEmpty()); - segment.Clear(); - EXPECT_TRUE(segment.IsEmpty()); - for (size_t i = 0; i < TestWorklist::Segment::kSize; i++) { - segment.Push(nullptr); + auto segment = CreateTemporarySegment(kMinSegmentSize); + segment->Push(nullptr); + EXPECT_FALSE(segment->IsEmpty()); + segment->Clear(); + EXPECT_TRUE(segment->IsEmpty()); + for (size_t i = 0; i < segment->Capacity(); i++) { + segment->Push(nullptr); } } TEST(WorkListTest, SegmentUpdateFalse) { - TestWorklist::Segment segment; + auto segment = CreateTemporarySegment(kMinSegmentSize); SomeObject* object; object = reinterpret_cast(&object); - segment.Push(object); - segment.Update([](SomeObject* object, SomeObject** out) { return false; }); - EXPECT_TRUE(segment.IsEmpty()); + segment->Push(object); + segment->Update([](SomeObject* object, SomeObject** out) { return false; }); + EXPECT_TRUE(segment->IsEmpty()); } TEST(WorkListTest, SegmentUpdate) { - TestWorklist::Segment segment; + auto segment = CreateTemporarySegment(kMinSegmentSize); SomeObject* objectA; objectA = reinterpret_cast(&objectA); SomeObject* objectB; objectB = reinterpret_cast(&objectB); - segment.Push(objectA); - segment.Update([objectB](SomeObject* object, SomeObject** out) { + segment->Push(objectA); + segment->Update([objectB](SomeObject* object, SomeObject** out) { *out = objectB; return true; }); SomeObject* object; - segment.Pop(&object); + segment->Pop(&object); EXPECT_EQ(object, objectB); } @@ -131,22 +139,22 @@ TEST(WorkListTest, LocalClear) { SomeObject* object; object = reinterpret_cast(&object); // Check push segment: - EXPECT_TRUE(worklist_local.IsEmpty()); + EXPECT_TRUE(worklist_local.IsLocalEmpty()); worklist_local.Push(object); - EXPECT_FALSE(worklist_local.IsEmpty()); + EXPECT_FALSE(worklist_local.IsLocalEmpty()); worklist_local.Clear(); - EXPECT_TRUE(worklist_local.IsEmpty()); + EXPECT_TRUE(worklist_local.IsLocalEmpty()); // Check pop segment: worklist_local.Push(object); worklist_local.Push(object); - EXPECT_FALSE(worklist_local.IsEmpty()); + EXPECT_FALSE(worklist_local.IsLocalEmpty()); worklist_local.Publish(); - EXPECT_TRUE(worklist_local.IsEmpty()); + EXPECT_TRUE(worklist_local.IsLocalEmpty()); SomeObject* retrieved; worklist_local.Pop(&retrieved); - EXPECT_FALSE(worklist_local.IsEmpty()); + EXPECT_FALSE(worklist_local.IsLocalEmpty()); worklist_local.Clear(); - EXPECT_TRUE(worklist_local.IsEmpty()); + EXPECT_TRUE(worklist_local.IsLocalEmpty()); } TEST(WorkListTest, GlobalUpdateNull) { @@ -154,7 +162,7 @@ TEST(WorkListTest, GlobalUpdateNull) { TestWorklist::Local worklist_local(&worklist); SomeObject* object; object = reinterpret_cast(&object); - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local.Push(object); } worklist_local.Push(object); @@ -173,10 +181,10 @@ TEST(WorkListTest, GlobalUpdate) { objectB = reinterpret_cast(&objectB); SomeObject* objectC = nullptr; objectC = reinterpret_cast(&objectC); - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local.Push(objectA); } - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local.Push(objectB); } worklist_local.Push(objectA); @@ -188,7 +196,7 @@ TEST(WorkListTest, GlobalUpdate) { } return false; }); - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { SomeObject* object; EXPECT_TRUE(worklist_local.Pop(&object)); EXPECT_EQ(object, objectC); @@ -241,17 +249,14 @@ TEST(WorkListTest, SingleSegmentSteal) { TestWorklist::Local worklist_local1(&worklist); TestWorklist::Local worklist_local2(&worklist); SomeObject dummy; - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local1.Push(&dummy); } - SomeObject* retrieved = nullptr; - // One more push/pop to publish the full segment. - worklist_local1.Push(nullptr); - EXPECT_TRUE(worklist_local1.Pop(&retrieved)); - EXPECT_EQ(nullptr, retrieved); + worklist_local1.Publish(); EXPECT_EQ(1U, worklist.Size()); // Stealing. - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + SomeObject* retrieved = nullptr; + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { EXPECT_TRUE(worklist_local2.Pop(&retrieved)); EXPECT_EQ(&dummy, retrieved); EXPECT_FALSE(worklist_local1.Pop(&retrieved)); @@ -267,20 +272,17 @@ TEST(WorkListTest, MultipleSegmentsStolen) { TestWorklist::Local worklist_local3(&worklist); SomeObject dummy1; SomeObject dummy2; - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local1.Push(&dummy1); } - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + worklist_local1.Publish(); + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local1.Push(&dummy2); } - SomeObject* retrieved = nullptr; - SomeObject dummy3; - // One more push/pop to publish the full segment. - worklist_local1.Push(&dummy3); - EXPECT_TRUE(worklist_local1.Pop(&retrieved)); - EXPECT_EQ(&dummy3, retrieved); + worklist_local1.Publish(); EXPECT_EQ(2U, worklist.Size()); // Stealing. + SomeObject* retrieved = nullptr; EXPECT_TRUE(worklist_local2.Pop(&retrieved)); SomeObject* const expect_bag2 = retrieved; EXPECT_TRUE(worklist_local3.Pop(&retrieved)); @@ -289,12 +291,12 @@ TEST(WorkListTest, MultipleSegmentsStolen) { 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 < TestWorklist::kSegmentSize; i++) { + for (size_t i = 1; i < TestWorklist::kMinSegmentSizeForTesting; i++) { EXPECT_TRUE(worklist_local2.Pop(&retrieved)); EXPECT_EQ(expect_bag2, retrieved); EXPECT_FALSE(worklist_local1.Pop(&retrieved)); } - for (size_t i = 1; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 1; i < TestWorklist::kMinSegmentSizeForTesting; i++) { EXPECT_TRUE(worklist_local3.Pop(&retrieved)); EXPECT_EQ(expect_bag3, retrieved); EXPECT_FALSE(worklist_local1.Pop(&retrieved)); @@ -306,15 +308,11 @@ TEST(WorkListTest, MergeGlobalPool) { TestWorklist worklist1; TestWorklist::Local worklist_local1(&worklist1); SomeObject dummy; - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { worklist_local1.Push(&dummy); } - SomeObject* retrieved = nullptr; // One more push/pop to publish the full segment. - worklist_local1.Push(nullptr); - EXPECT_TRUE(worklist_local1.Pop(&retrieved)); - EXPECT_EQ(nullptr, retrieved); - EXPECT_EQ(1U, worklist1.Size()); + worklist_local1.Publish(); // Merging global pool into a new Worklist. TestWorklist worklist2; TestWorklist::Local worklist_local2(&worklist2); @@ -322,7 +320,8 @@ TEST(WorkListTest, MergeGlobalPool) { worklist2.Merge(&worklist1); EXPECT_EQ(1U, worklist2.Size()); EXPECT_FALSE(worklist2.IsEmpty()); - for (size_t i = 0; i < TestWorklist::kSegmentSize; i++) { + SomeObject* retrieved = nullptr; + for (size_t i = 0; i < TestWorklist::kMinSegmentSizeForTesting; i++) { EXPECT_TRUE(worklist_local2.Pop(&retrieved)); EXPECT_EQ(&dummy, retrieved); EXPECT_FALSE(worklist_local1.Pop(&retrieved));