Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,81 @@
// Copyright 2022 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/active-system-pages.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace heap {
namespace base {
TEST(ActiveSystemPagesTest, Add) {
ActiveSystemPages pages;
const size_t kPageSizeBits = 0;
EXPECT_EQ(pages.Add(0, 1, kPageSizeBits), size_t{1});
EXPECT_EQ(pages.Add(1, 2, kPageSizeBits), size_t{1});
EXPECT_EQ(pages.Add(63, 64, kPageSizeBits), size_t{1});
EXPECT_EQ(pages.Size(kPageSizeBits), size_t{3});
// Try to add page a second time.
EXPECT_EQ(pages.Add(0, 2, kPageSizeBits), size_t{0});
}
TEST(ActiveSystemPagesTest, AddUnalignedRange) {
ActiveSystemPages pages;
const size_t kPageSizeBits = 12;
const size_t kPageSize = size_t{1} << kPageSizeBits;
const size_t kWordSize = 8;
EXPECT_EQ(pages.Add(0, kPageSize + kWordSize, kPageSizeBits), size_t{2});
EXPECT_EQ(pages.Add(3 * kPageSize - kWordSize, 3 * kPageSize, kPageSizeBits),
size_t{1});
EXPECT_EQ(pages.Add(kPageSize + kWordSize, 3 * kPageSize - kWordSize,
kPageSizeBits),
size_t{0});
EXPECT_EQ(pages.Size(kPageSizeBits), size_t{3} * kPageSize);
}
TEST(ActiveSystemPagesTest, AddFullBitset) {
ActiveSystemPages pages;
const size_t kPageSizeBits = 0;
EXPECT_EQ(pages.Add(0, 64, kPageSizeBits), size_t{64});
EXPECT_EQ(pages.Add(0, 64, kPageSizeBits), size_t{0});
EXPECT_EQ(pages.Size(kPageSizeBits), size_t{64});
}
TEST(ActiveSystemPagesTest, Reduce) {
ActiveSystemPages original;
const size_t kPageSizeBits = 0;
EXPECT_EQ(original.Add(0, 3, kPageSizeBits), size_t{3});
ActiveSystemPages updated;
EXPECT_EQ(updated.Add(1, 3, kPageSizeBits), size_t{2});
EXPECT_EQ(original.Reduce(updated), size_t{1});
}
TEST(ActiveSystemPagesTest, ReduceFullBitset) {
ActiveSystemPages original;
const size_t kPageSizeBits = 0;
EXPECT_EQ(original.Add(0, 64, kPageSizeBits), size_t{64});
ActiveSystemPages updated;
EXPECT_EQ(updated.Add(63, 64, kPageSizeBits), size_t{1});
EXPECT_EQ(original.Reduce(updated), size_t{63});
}
TEST(ActiveSystemPagesTest, Clear) {
ActiveSystemPages pages;
const size_t kPageSizeBits = 0;
EXPECT_EQ(pages.Add(0, 64, kPageSizeBits), size_t{64});
EXPECT_EQ(pages.Clear(), size_t{64});
EXPECT_EQ(pages.Size(kPageSizeBits), size_t{0});
EXPECT_EQ(pages.Add(0, 2, kPageSizeBits), size_t{2});
EXPECT_EQ(pages.Clear(), size_t{2});
EXPECT_EQ(pages.Size(kPageSizeBits), size_t{0});
}
} // namespace base
} // namespace heap

View File

@ -0,0 +1,198 @@
// Copyright 2022 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/basic-slot-set.h"
#include <limits>
#include <map>
#include "testing/gtest/include/gtest/gtest.h"
namespace heap {
namespace base {
static constexpr size_t kTestGranularity = sizeof(void*);
using TestSlotSet = ::heap::base::BasicSlotSet<kTestGranularity>;
static constexpr size_t kTestPageSize = 1 << 17;
static constexpr size_t kBucketsTestPage =
TestSlotSet::BucketsForSize(kTestPageSize);
TEST(BasicSlotSet, InsertAndLookup1) {
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
EXPECT_FALSE(set->Lookup(i));
}
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(i);
}
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
EXPECT_TRUE(set->Lookup(i));
}
TestSlotSet::Delete(set);
}
TEST(BasicSlotSet, InsertAndLookup2) {
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 7 == 0) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(i);
}
}
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 7 == 0) {
EXPECT_TRUE(set->Lookup(i));
} else {
EXPECT_FALSE(set->Lookup(i));
}
}
TestSlotSet::Delete(set);
}
TEST(BasicSlotSet, Iterate) {
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 7 == 0) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(i);
}
}
set->Iterate(
0, 0, kBucketsTestPage,
[](uintptr_t slot) {
if (slot % 3 == 0) {
return KEEP_SLOT;
} else {
return REMOVE_SLOT;
}
},
TestSlotSet::KEEP_EMPTY_BUCKETS);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 21 == 0) {
EXPECT_TRUE(set->Lookup(i));
} else {
EXPECT_FALSE(set->Lookup(i));
}
}
TestSlotSet::Delete(set);
}
TEST(BasicSlotSet, IterateFromHalfway) {
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 7 == 0) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(i);
}
}
set->Iterate(
0, kBucketsTestPage / 2, kBucketsTestPage,
[](uintptr_t slot) {
if (slot % 3 == 0) {
return KEEP_SLOT;
} else {
return REMOVE_SLOT;
}
},
TestSlotSet::KEEP_EMPTY_BUCKETS);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i < kTestPageSize / 2 && i % 7 == 0) {
EXPECT_TRUE(set->Lookup(i));
} else if (i >= kTestPageSize / 2 && i % 21 == 0) {
EXPECT_TRUE(set->Lookup(i));
} else {
EXPECT_FALSE(set->Lookup(i));
}
}
TestSlotSet::Delete(set);
}
TEST(BasicSlotSet, Remove) {
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 7 == 0) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(i);
}
}
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 3 != 0) {
set->Remove(i);
}
}
for (size_t i = 0; i < kTestPageSize; i += kTestGranularity) {
if (i % 21 == 0) {
EXPECT_TRUE(set->Lookup(i));
} else {
EXPECT_FALSE(set->Lookup(i));
}
}
TestSlotSet::Delete(set);
}
namespace {
void CheckRemoveRangeOn(uint32_t start, uint32_t end) {
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
uint32_t first = start == 0 ? 0 : start - kTestGranularity;
uint32_t last = end == kTestPageSize ? end - kTestGranularity : end;
for (const auto mode :
{TestSlotSet::FREE_EMPTY_BUCKETS, TestSlotSet::KEEP_EMPTY_BUCKETS}) {
for (uint32_t i = first; i <= last; i += kTestGranularity) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(i);
}
set->RemoveRange(start, end, kBucketsTestPage, mode);
if (first != start) {
EXPECT_TRUE(set->Lookup(first));
}
if (last == end) {
EXPECT_TRUE(set->Lookup(last));
}
for (size_t i = start; i < end; i += kTestGranularity) {
EXPECT_FALSE(set->Lookup(i));
}
}
TestSlotSet::Delete(set);
}
} // namespace
TEST(BasicSlotSet, RemoveRange) {
CheckRemoveRangeOn(0, kTestPageSize);
CheckRemoveRangeOn(1 * kTestGranularity, 1023 * kTestGranularity);
for (uint32_t start = 0; start <= 32; start++) {
CheckRemoveRangeOn(start * kTestGranularity,
(start + 1) * kTestGranularity);
CheckRemoveRangeOn(start * kTestGranularity,
(start + 2) * kTestGranularity);
const uint32_t kEnds[] = {32, 64, 100, 128, 1024, 1500, 2048};
for (size_t i = 0; i < sizeof(kEnds) / sizeof(uint32_t); i++) {
for (int k = -3; k <= 3; k++) {
uint32_t end = (kEnds[i] + k);
if (start < end) {
CheckRemoveRangeOn(start * kTestGranularity, end * kTestGranularity);
}
}
}
}
TestSlotSet* set = TestSlotSet::Allocate(kBucketsTestPage);
for (const auto mode :
{TestSlotSet::FREE_EMPTY_BUCKETS, TestSlotSet::KEEP_EMPTY_BUCKETS}) {
set->Insert<TestSlotSet::AccessMode::ATOMIC>(kTestPageSize / 2);
set->RemoveRange(0, kTestPageSize, kBucketsTestPage, mode);
for (uint32_t i = 0; i < kTestPageSize; i += kTestGranularity) {
EXPECT_FALSE(set->Lookup(i));
}
}
TestSlotSet::Delete(set);
}
} // namespace base
} // namespace heap

View File

@ -0,0 +1,134 @@
// Copyright 2023 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/bytes.h"
#include <optional>
#include "testing/gtest/include/gtest/gtest.h"
namespace heap::base {
TEST(BytesAndDurationTest, MakeBytesAndDuration) {
const auto bad =
BytesAndDuration(17, v8::base::TimeDelta::FromMilliseconds(35));
EXPECT_EQ(bad.bytes, 17u);
EXPECT_EQ(bad.duration.InMilliseconds(), 35);
}
TEST(BytesAndDurationTest, InitialAsAverage) {
BytesAndDurationBuffer buffer;
EXPECT_DOUBLE_EQ(
100.0 / 2,
*AverageSpeed(
buffer,
BytesAndDuration(100, v8::base::TimeDelta::FromMilliseconds(2)),
std::nullopt));
}
TEST(BytesAndDurationTest, SelectedDuration) {
BytesAndDurationBuffer buffer;
// The entry will be ignored because of the selected duration below filtering
// for the last 2ms.
buffer.Push(BytesAndDuration(100, v8::base::TimeDelta::FromMilliseconds(8)));
EXPECT_DOUBLE_EQ(
100.0 / 2,
*AverageSpeed(
buffer,
BytesAndDuration(100, v8::base::TimeDelta::FromMilliseconds(2)),
v8::base::TimeDelta::FromMilliseconds(2)));
}
TEST(BytesAndDurationTest, Empty) {
BytesAndDurationBuffer buffer;
EXPECT_EQ(std::nullopt,
AverageSpeed(buffer, BytesAndDuration(), std::nullopt));
}
TEST(BytesAndDurationTest, Clear) {
BytesAndDurationBuffer buffer;
buffer.Push(BytesAndDuration(100, v8::base::TimeDelta::FromMilliseconds(2)));
EXPECT_DOUBLE_EQ(100.0 / 2,
*AverageSpeed(buffer, BytesAndDuration(), std::nullopt));
buffer.Clear();
EXPECT_EQ(std::nullopt,
AverageSpeed(buffer, BytesAndDuration(), std::nullopt));
}
TEST(BytesAndDurationTest, MaxSpeed) {
BytesAndDurationBuffer buffer;
static constexpr size_t kMaxBytesPerMs = 1024;
buffer.Push(BytesAndDuration(kMaxBytesPerMs,
v8::base::TimeDelta::FromMillisecondsD(0.5)));
const double bounded_speed = *AverageSpeed(buffer, BytesAndDuration(),
std::nullopt, 0, kMaxBytesPerMs);
EXPECT_DOUBLE_EQ(double{kMaxBytesPerMs}, bounded_speed);
}
TEST(BytesAndDurationTest, MinSpeed) {
BytesAndDurationBuffer buffer;
static constexpr size_t kMinBytesPerMs = 1;
buffer.Push(BytesAndDuration(kMinBytesPerMs,
v8::base::TimeDelta::FromMillisecondsD(2)));
const double bounded_speed =
*AverageSpeed(buffer, BytesAndDuration(), std::nullopt, kMinBytesPerMs);
EXPECT_DOUBLE_EQ(double{kMinBytesPerMs}, bounded_speed);
}
TEST(BytesAndDurationTest, RingBufferAverage) {
BytesAndDurationBuffer buffer;
size_t sum = 0;
for (size_t i = 0; i < BytesAndDurationBuffer::kSize; ++i) {
sum += i + 1;
buffer.Push(
BytesAndDuration(i + 1, v8::base::TimeDelta::FromMillisecondsD(1)));
EXPECT_DOUBLE_EQ(static_cast<double>(sum) / (i + 1),
*AverageSpeed(buffer, BytesAndDuration(), std::nullopt));
}
EXPECT_DOUBLE_EQ(static_cast<double>(sum) / BytesAndDurationBuffer::kSize,
*AverageSpeed(buffer, BytesAndDuration(), std::nullopt));
// Overflow the ring buffer.
buffer.Push(BytesAndDuration(100, v8::base::TimeDelta::FromMilliseconds(1)));
EXPECT_DOUBLE_EQ(
static_cast<double>(sum + 100 - 1) / BytesAndDurationBuffer::kSize,
*AverageSpeed(buffer, BytesAndDuration(), std::nullopt));
}
TEST(SmoothedBytesAndDuration, ZeroDelta) {
SmoothedBytesAndDuration smoothed_throughput(
v8::base::TimeDelta::FromSeconds(1));
EXPECT_EQ(smoothed_throughput.GetThroughput(), 0);
// NaN rate is ignored.
smoothed_throughput.Update(BytesAndDuration(10, v8::base::TimeDelta()));
EXPECT_EQ(smoothed_throughput.GetThroughput(), 0);
}
TEST(SmoothedBytesAndDuration, Update) {
SmoothedBytesAndDuration smoothed_throughput(
v8::base::TimeDelta::FromMilliseconds(1));
EXPECT_EQ(smoothed_throughput.GetThroughput(), 0);
// Smoothed update from the original throughput, with 1ms half-life.
smoothed_throughput.Update(
BytesAndDuration(10, v8::base::TimeDelta::FromMilliseconds(1)));
EXPECT_EQ(smoothed_throughput.GetThroughput(), 5.0);
// After long enough, the throughput will converge.
smoothed_throughput.Update(
BytesAndDuration(1000, v8::base::TimeDelta::FromMilliseconds(1000)));
EXPECT_EQ(smoothed_throughput.GetThroughput(), 1.0);
// The throughput decays with a half-life of 1ms.
EXPECT_EQ(smoothed_throughput.GetThroughput(
v8::base::TimeDelta::FromMilliseconds(1)),
0.5);
smoothed_throughput.Update(
BytesAndDuration(0, v8::base::TimeDelta::FromMilliseconds(1)));
EXPECT_EQ(smoothed_throughput.GetThroughput(), 0.5);
}
} // namespace heap::base

View File

@ -0,0 +1,150 @@
// 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/incremental-marking-schedule.h"
#include "src/base/platform/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace heap::base {
namespace {
constexpr size_t kZeroBytesStep = 0;
// Minimum number of bytes that should be marked during an incremental
// marking step.
constexpr size_t kMinimumMarkedBytesPerIncrementalStep =
IncrementalMarkingSchedule::kStepSizeWhenNotMakingProgress;
class IncrementalMarkingScheduleTest : public ::testing::Test {
public:
static constexpr size_t kEstimatedLiveSize =
100 * kMinimumMarkedBytesPerIncrementalStep;
};
const v8::base::TimeDelta kHalfEstimatedMarkingTime =
v8::base::TimeDelta::FromMillisecondsD(
IncrementalMarkingSchedule::kEstimatedMarkingTime.InMillisecondsF() *
0.5);
} // namespace
TEST_F(IncrementalMarkingScheduleTest, FirstStepReturnsDefaultDuration) {
auto schedule =
IncrementalMarkingSchedule::CreateWithMarkedBytesPerStepForTesting(
kMinimumMarkedBytesPerIncrementalStep);
schedule->NotifyIncrementalMarkingStart();
schedule->SetElapsedTimeForTesting(v8::base::TimeDelta::FromMilliseconds(0));
EXPECT_EQ(kMinimumMarkedBytesPerIncrementalStep,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
TEST_F(IncrementalMarkingScheduleTest, EmptyStepDuration) {
auto schedule = IncrementalMarkingSchedule::Create();
schedule->NotifyIncrementalMarkingStart();
schedule->SetElapsedTimeForTesting(v8::base::TimeDelta::FromMilliseconds(0));
// Make some progress on the marker to avoid returning step size for no
// progress.
schedule->AddMutatorThreadMarkedBytes(
IncrementalMarkingSchedule::kStepSizeWhenNotMakingProgress);
EXPECT_EQ(kZeroBytesStep,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
// If marking is not behind schedule and very small time passed between steps
// the oracle should return the minimum step duration.
TEST_F(IncrementalMarkingScheduleTest, NoTimePassedReturnsMinimumDuration) {
auto schedule =
IncrementalMarkingSchedule::CreateWithMarkedBytesPerStepForTesting(
kMinimumMarkedBytesPerIncrementalStep);
schedule->NotifyIncrementalMarkingStart();
// Add incrementally marked bytes to tell oracle this is not the first step.
schedule->AddMutatorThreadMarkedBytes(kMinimumMarkedBytesPerIncrementalStep);
schedule->SetElapsedTimeForTesting(v8::base::TimeDelta::FromMilliseconds(0));
EXPECT_EQ(kMinimumMarkedBytesPerIncrementalStep,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
TEST_F(IncrementalMarkingScheduleTest, OracleDoesntExccedMaximumStepDuration) {
auto schedule =
IncrementalMarkingSchedule::CreateWithMarkedBytesPerStepForTesting(
kMinimumMarkedBytesPerIncrementalStep);
schedule->NotifyIncrementalMarkingStart();
// Add incrementally marked bytes to tell oracle this is not the first step.
// Add at least `kStepSizeWhenNotMakingProgress` bytes or otherwise we'd get
// the step size for not making progress.
static constexpr size_t kMarkedBytes =
IncrementalMarkingSchedule::kStepSizeWhenNotMakingProgress;
schedule->AddMutatorThreadMarkedBytes(kMarkedBytes);
schedule->SetElapsedTimeForTesting(
IncrementalMarkingSchedule::kEstimatedMarkingTime);
EXPECT_EQ(kEstimatedLiveSize - kMarkedBytes,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
TEST_F(IncrementalMarkingScheduleTest, AheadOfScheduleReturnsMinimumDuration) {
auto schedule =
IncrementalMarkingSchedule::CreateWithMarkedBytesPerStepForTesting(
kMinimumMarkedBytesPerIncrementalStep);
schedule->NotifyIncrementalMarkingStart();
// Add incrementally marked bytes to tell oracle this is not the first step.
schedule->AddMutatorThreadMarkedBytes(kMinimumMarkedBytesPerIncrementalStep);
schedule->AddConcurrentlyMarkedBytes(0.6 * kEstimatedLiveSize);
schedule->SetElapsedTimeForTesting(kHalfEstimatedMarkingTime);
EXPECT_EQ(kMinimumMarkedBytesPerIncrementalStep,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
TEST_F(IncrementalMarkingScheduleTest,
AheadOfScheduleReturnsMinimumDurationZeroStep) {
auto schedule = IncrementalMarkingSchedule::Create();
schedule->NotifyIncrementalMarkingStart();
// Add incrementally marked bytes to tell oracle this is not the first step.
schedule->AddMutatorThreadMarkedBytes(kMinimumMarkedBytesPerIncrementalStep);
schedule->AddConcurrentlyMarkedBytes(0.6 * kEstimatedLiveSize);
schedule->SetElapsedTimeForTesting(kHalfEstimatedMarkingTime);
EXPECT_EQ(kZeroBytesStep,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
TEST_F(IncrementalMarkingScheduleTest, BehindScheduleReturnsDelta) {
auto schedule =
IncrementalMarkingSchedule::CreateWithMarkedBytesPerStepForTesting(
kMinimumMarkedBytesPerIncrementalStep);
schedule->NotifyIncrementalMarkingStart();
schedule->AddMutatorThreadMarkedBytes(0.1 * kEstimatedLiveSize);
schedule->AddConcurrentlyMarkedBytes(0.25 * kEstimatedLiveSize);
schedule->SetElapsedTimeForTesting(kHalfEstimatedMarkingTime);
EXPECT_EQ(0.15 * kEstimatedLiveSize,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
schedule->AddConcurrentlyMarkedBytes(0.05 * kEstimatedLiveSize);
schedule->SetElapsedTimeForTesting(kHalfEstimatedMarkingTime);
EXPECT_EQ(0.1 * kEstimatedLiveSize,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
schedule->AddConcurrentlyMarkedBytes(0.05 * kEstimatedLiveSize);
schedule->SetElapsedTimeForTesting(kHalfEstimatedMarkingTime);
EXPECT_EQ(0.05 * kEstimatedLiveSize,
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize));
}
TEST_F(IncrementalMarkingScheduleTest, GetCurrentStepInfo) {
auto schedule =
IncrementalMarkingSchedule::CreateWithMarkedBytesPerStepForTesting(
kMinimumMarkedBytesPerIncrementalStep);
schedule->NotifyIncrementalMarkingStart();
schedule->AddMutatorThreadMarkedBytes(0.3 * kEstimatedLiveSize);
schedule->AddConcurrentlyMarkedBytes(0.4 * kEstimatedLiveSize);
schedule->SetElapsedTimeForTesting(kHalfEstimatedMarkingTime);
schedule->GetNextIncrementalStepDuration(kEstimatedLiveSize);
const auto step_info = schedule->GetCurrentStepInfo();
EXPECT_EQ(step_info.elapsed_time, kHalfEstimatedMarkingTime);
EXPECT_EQ(step_info.mutator_marked_bytes, 0.3 * kEstimatedLiveSize);
EXPECT_EQ(step_info.concurrent_marked_bytes, 0.4 * kEstimatedLiveSize);
EXPECT_EQ(step_info.marked_bytes(), 0.7 * kEstimatedLiveSize);
EXPECT_EQ(step_info.estimated_live_bytes, kEstimatedLiveSize);
EXPECT_NE(step_info.scheduled_delta_bytes(), 0);
}
} // namespace heap::base

View File

@ -0,0 +1,17 @@
// 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 "testing/gmock/include/gmock/gmock.h"
int main(int argc, char** argv) {
// Don't catch SEH exceptions and continue as the following tests might hang
// in an broken environment on windows.
GTEST_FLAG_SET(catch_exceptions, false);
// Most unit-tests are multi-threaded, so enable thread-safe death-tests.
GTEST_FLAG_SET(death_test_style, "threadsafe");
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,334 @@
// 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"
#include "testing/gtest/include/gtest/gtest.h"
namespace heap {
namespace base {
class SomeObject {};
constexpr size_t kMinSegmentSize = 64;
using TestWorklist = Worklist<SomeObject*, kMinSegmentSize>;
using Segment = TestWorklist::Segment;
auto CreateTemporarySegment(size_t min_segment_size) {
return std::unique_ptr<Segment, void (*)(Segment*)>(
Segment::Create(min_segment_size),
[](Segment* s) { Segment::Delete(s); });
}
TEST(WorkListTest, SegmentCreate) {
auto segment = CreateTemporarySegment(kMinSegmentSize);
EXPECT_TRUE(segment->IsEmpty());
EXPECT_EQ(0u, segment->Size());
EXPECT_FALSE(segment->IsFull());
}
TEST(WorkListTest, SegmentPush) {
auto segment = CreateTemporarySegment(kMinSegmentSize);
EXPECT_EQ(0u, segment->Size());
segment->Push(nullptr);
EXPECT_EQ(1u, segment->Size());
}
TEST(WorkListTest, SegmentPushPop) {
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());
EXPECT_EQ(nullptr, object);
}
TEST(WorkListTest, SegmentIsEmpty) {
auto segment = CreateTemporarySegment(kMinSegmentSize);
EXPECT_TRUE(segment->IsEmpty());
segment->Push(nullptr);
EXPECT_FALSE(segment->IsEmpty());
}
TEST(WorkListTest, SegmentIsFull) {
auto segment = CreateTemporarySegment(kMinSegmentSize);
EXPECT_FALSE(segment->IsFull());
for (size_t i = 0; i < segment->Capacity(); i++) {
segment->Push(nullptr);
}
EXPECT_TRUE(segment->IsFull());
}
TEST(WorkListTest, SegmentClear) {
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) {
auto segment = CreateTemporarySegment(kMinSegmentSize);
SomeObject* object;
object = reinterpret_cast<SomeObject*>(&object);
segment->Push(object);
segment->Update([](SomeObject* object, SomeObject** out) { return false; });
EXPECT_TRUE(segment->IsEmpty());
}
TEST(WorkListTest, SegmentUpdate) {
auto segment = CreateTemporarySegment(kMinSegmentSize);
SomeObject* objectA;
objectA = reinterpret_cast<SomeObject*>(&objectA);
SomeObject* objectB;
objectB = reinterpret_cast<SomeObject*>(&objectB);
segment->Push(objectA);
segment->Update([objectB](SomeObject* object, SomeObject** out) {
*out = objectB;
return true;
});
SomeObject* object;
segment->Pop(&object);
EXPECT_EQ(object, objectB);
}
TEST(WorkListTest, CreateEmpty) {
TestWorklist worklist;
TestWorklist::Local worklist_local(worklist);
EXPECT_TRUE(worklist_local.IsLocalEmpty());
EXPECT_TRUE(worklist.IsEmpty());
}
TEST(WorkListTest, LocalPushPop) {
TestWorklist worklist;
TestWorklist::Local worklist_local(worklist);
SomeObject dummy;
SomeObject* retrieved = nullptr;
worklist_local.Push(&dummy);
EXPECT_FALSE(worklist_local.IsLocalEmpty());
EXPECT_TRUE(worklist_local.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
}
TEST(WorkListTest, LocalPushStaysPrivate) {
TestWorklist worklist;
TestWorklist::Local worklist_local1(worklist);
TestWorklist::Local worklist_local2(worklist);
SomeObject dummy;
SomeObject* retrieved = nullptr;
EXPECT_TRUE(worklist.IsEmpty());
EXPECT_EQ(0U, worklist.Size());
worklist_local1.Push(&dummy);
EXPECT_EQ(0U, worklist.Size());
EXPECT_FALSE(worklist_local2.Pop(&retrieved));
EXPECT_EQ(nullptr, retrieved);
EXPECT_TRUE(worklist_local1.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_EQ(0U, worklist.Size());
}
TEST(WorkListTest, LocalClear) {
TestWorklist worklist;
TestWorklist::Local worklist_local(worklist);
SomeObject* object;
object = reinterpret_cast<SomeObject*>(&object);
// Check push segment:
EXPECT_TRUE(worklist_local.IsLocalEmpty());
worklist_local.Push(object);
EXPECT_FALSE(worklist_local.IsLocalEmpty());
worklist_local.Clear();
EXPECT_TRUE(worklist_local.IsLocalEmpty());
// Check pop segment:
worklist_local.Push(object);
worklist_local.Push(object);
EXPECT_FALSE(worklist_local.IsLocalEmpty());
worklist_local.Publish();
EXPECT_TRUE(worklist_local.IsLocalEmpty());
SomeObject* retrieved;
worklist_local.Pop(&retrieved);
EXPECT_FALSE(worklist_local.IsLocalEmpty());
worklist_local.Clear();
EXPECT_TRUE(worklist_local.IsLocalEmpty());
}
TEST(WorkListTest, GlobalUpdateNull) {
TestWorklist worklist;
TestWorklist::Local worklist_local(worklist);
SomeObject* object;
object = reinterpret_cast<SomeObject*>(&object);
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
worklist_local.Push(object);
}
worklist_local.Push(object);
worklist_local.Publish();
worklist.Update([](SomeObject* object, SomeObject** out) { return false; });
EXPECT_TRUE(worklist.IsEmpty());
EXPECT_EQ(0U, worklist.Size());
}
TEST(WorkListTest, GlobalUpdate) {
TestWorklist worklist;
TestWorklist::Local worklist_local(worklist);
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::kMinSegmentSize; i++) {
worklist_local.Push(objectA);
}
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
worklist_local.Push(objectB);
}
worklist_local.Push(objectA);
worklist_local.Publish();
worklist.Update([objectA, objectC](SomeObject* object, SomeObject** out) {
if (object != objectA) {
*out = objectC;
return true;
}
return false;
});
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
SomeObject* object;
EXPECT_TRUE(worklist_local.Pop(&object));
EXPECT_EQ(object, objectC);
}
}
TEST(WorkListTest, FlushToGlobalPushSegment) {
TestWorklist worklist;
TestWorklist::Local worklist_local0(worklist);
TestWorklist::Local worklist_local1(worklist);
SomeObject* object = nullptr;
SomeObject* objectA = nullptr;
objectA = reinterpret_cast<SomeObject*>(&objectA);
worklist_local0.Push(objectA);
worklist_local0.Publish();
EXPECT_EQ(1U, worklist.Size());
EXPECT_TRUE(worklist_local1.Pop(&object));
}
TEST(WorkListTest, FlushToGlobalPopSegment) {
TestWorklist worklist;
TestWorklist::Local worklist_local0(worklist);
TestWorklist::Local worklist_local1(worklist);
SomeObject* object = nullptr;
SomeObject* objectA = nullptr;
objectA = reinterpret_cast<SomeObject*>(&objectA);
worklist_local0.Push(objectA);
worklist_local0.Push(objectA);
worklist_local0.Pop(&object);
worklist_local0.Publish();
EXPECT_EQ(1U, worklist.Size());
EXPECT_TRUE(worklist_local1.Pop(&object));
}
TEST(WorkListTest, Clear) {
TestWorklist worklist;
TestWorklist::Local worklist_local(worklist);
SomeObject* object;
object = reinterpret_cast<SomeObject*>(&object);
worklist_local.Push(object);
worklist_local.Publish();
EXPECT_EQ(1U, worklist.Size());
worklist.Clear();
EXPECT_TRUE(worklist.IsEmpty());
EXPECT_EQ(0U, worklist.Size());
}
TEST(WorkListTest, SingleSegmentSteal) {
TestWorklist worklist;
TestWorklist::Local worklist_local1(worklist);
TestWorklist::Local worklist_local2(worklist);
SomeObject dummy;
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
worklist_local1.Push(&dummy);
}
worklist_local1.Publish();
EXPECT_EQ(1U, worklist.Size());
// Stealing.
SomeObject* retrieved = nullptr;
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
EXPECT_TRUE(worklist_local2.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_FALSE(worklist_local1.Pop(&retrieved));
}
EXPECT_TRUE(worklist.IsEmpty());
EXPECT_EQ(0U, worklist.Size());
}
TEST(WorkListTest, MultipleSegmentsStolen) {
TestWorklist worklist;
TestWorklist::Local worklist_local1(worklist);
TestWorklist::Local worklist_local2(worklist);
TestWorklist::Local worklist_local3(worklist);
SomeObject dummy1;
SomeObject dummy2;
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
worklist_local1.Push(&dummy1);
}
worklist_local1.Publish();
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
worklist_local1.Push(&dummy2);
}
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));
SomeObject* const expect_bag3 = retrieved;
EXPECT_EQ(0U, worklist.Size());
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::kMinSegmentSize; 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::kMinSegmentSize; i++) {
EXPECT_TRUE(worklist_local3.Pop(&retrieved));
EXPECT_EQ(expect_bag3, retrieved);
EXPECT_FALSE(worklist_local1.Pop(&retrieved));
}
EXPECT_TRUE(worklist.IsEmpty());
}
TEST(WorkListTest, MergeGlobalPool) {
TestWorklist worklist1;
TestWorklist::Local worklist_local1(worklist1);
SomeObject dummy;
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
worklist_local1.Push(&dummy);
}
// One more push/pop to publish the full segment.
worklist_local1.Publish();
// Merging global pool into a new Worklist.
TestWorklist worklist2;
TestWorklist::Local worklist_local2(worklist2);
EXPECT_EQ(0U, worklist2.Size());
worklist2.Merge(worklist1);
EXPECT_EQ(1U, worklist2.Size());
EXPECT_FALSE(worklist2.IsEmpty());
SomeObject* retrieved = nullptr;
for (size_t i = 0; i < TestWorklist::kMinSegmentSize; i++) {
EXPECT_TRUE(worklist_local2.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_FALSE(worklist_local1.Pop(&retrieved));
}
EXPECT_TRUE(worklist1.IsEmpty());
EXPECT_TRUE(worklist2.IsEmpty());
}
} // namespace base
} // namespace heap