blob: a4818fdb32ac11f9a58b7d89777051b038b5937f [file] [log] [blame]
// Copyright 2018 The Chromium 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/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
class ThreadStateSchedulingTest : public testing::Test {
public:
void SetUp() override {
state_ = ThreadState::Current();
ClearOutOldGarbage();
initial_gc_age_ = state_->GcAge();
}
void TearDown() override {
features_backup_.Restore();
PreciselyCollectGarbage();
EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
EXPECT_FALSE(state_->IsMarkingInProgress());
EXPECT_FALSE(state_->IsSweepingInProgress());
}
void StartIncrementalMarkingForIdleGC() {
RuntimeEnabledFeatures::SetHeapIncrementalMarkingEnabled(true);
EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
state_->ScheduleIdleGC();
RunIdleGCTask();
EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
state_->GetGCState());
EXPECT_TRUE(state_->IsMarkingInProgress());
}
void RunIdleGCTask() {
// Simulate running idle GC task, instead of actually running the posted
// task.
EXPECT_EQ(ThreadState::kIdleGCScheduled, state_->GetGCState());
state_->PerformIdleGC(TimeTicks::Max());
}
void StartLazySweepingForPreciseGC() {
EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
state_->SchedulePreciseGC();
EXPECT_EQ(ThreadState::kPreciseGCScheduled, state_->GetGCState());
RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
EXPECT_TRUE(state_->IsSweepingInProgress());
EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
}
void RunScheduledGC(BlinkGC::StackState stack_state) {
state_->RunScheduledGC(stack_state);
}
// Counter that is incremented when sweep finishes.
int GCCount() { return state_->GcAge() - initial_gc_age_; }
ThreadState* state() { return state_; }
private:
ThreadState* state_;
int initial_gc_age_;
RuntimeEnabledFeatures::Backup features_backup_;
};
TEST_F(ThreadStateSchedulingTest, ScheduleIdleGCAgain) {
ThreadStateSchedulingTest* test = this;
EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
test->state()->ScheduleIdleGC();
EXPECT_EQ(ThreadState::kIdleGCScheduled, test->state()->GetGCState());
// Calling ScheduleIdleGC() while an idle GC is scheduled will do nothing.
test->state()->ScheduleIdleGC();
EXPECT_EQ(ThreadState::kIdleGCScheduled, test->state()->GetGCState());
EXPECT_EQ(0, test->GCCount());
}
TEST_F(ThreadStateSchedulingTest, ScheduleIncrementalV8FollowupGCAgain) {
ThreadStateSchedulingTest* test = this;
EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
test->state()->ScheduleIncrementalGC(
BlinkGC::GCReason::kIncrementalV8FollowupGC);
EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
// Calling ScheduleIncrementalV8FollowupGC() while one is already scheduled
// will do nothing.
test->state()->ScheduleIncrementalGC(
BlinkGC::GCReason::kIncrementalV8FollowupGC);
EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
EXPECT_EQ(0, test->GCCount());
}
TEST_F(ThreadStateSchedulingTest, ScheduleIdleGCWhileIncrementalMarking) {
ThreadStateSchedulingTest* test = this;
test->StartIncrementalMarkingForIdleGC();
EXPECT_TRUE(test->state()->IsMarkingInProgress());
EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
test->state()->GetGCState());
// Calling ScheduleIdleGC() while an idle GC is scheduled should do nothing.
test->state()->ScheduleIdleGC();
EXPECT_TRUE(test->state()->IsMarkingInProgress());
EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
test->state()->GetGCState());
}
TEST_F(ThreadStateSchedulingTest, ScheduleIdleGCWhileLazySweeping) {
ThreadStateSchedulingTest* test = this;
test->StartLazySweepingForPreciseGC();
test->state()->ScheduleIdleGC();
// Scheduling an idle GC should finish lazy sweeping.
EXPECT_FALSE(test->state()->IsSweepingInProgress());
EXPECT_EQ(ThreadState::kIdleGCScheduled, test->state()->GetGCState());
}
TEST_F(ThreadStateSchedulingTest, SchedulePreciseGCWhileLazySweeping) {
ThreadStateSchedulingTest* test = this;
test->StartLazySweepingForPreciseGC();
test->state()->SchedulePreciseGC();
// Scheduling a precise GC should finish lazy sweeping.
EXPECT_FALSE(test->state()->IsSweepingInProgress());
EXPECT_EQ(ThreadState::kPreciseGCScheduled, test->state()->GetGCState());
}
TEST_F(ThreadStateSchedulingTest,
ScheduleIncrementalV8FollowupGCWhileLazySweeping) {
ThreadStateSchedulingTest* test = this;
test->StartLazySweepingForPreciseGC();
test->state()->ScheduleIncrementalGC(
BlinkGC::GCReason::kIncrementalV8FollowupGC);
// Scheduling a IncrementalV8FollowupGC should finish lazy sweeping.
EXPECT_FALSE(test->state()->IsSweepingInProgress());
EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
}
TEST_F(ThreadStateSchedulingTest, SchedulePreciseGCWhileIncrementalMarking) {
ThreadStateSchedulingTest* test = this;
test->StartIncrementalMarkingForIdleGC();
test->state()->SchedulePreciseGC();
// Scheduling a precise GC should cancel incremental marking tasks.
EXPECT_EQ(ThreadState::kPreciseGCScheduled, test->state()->GetGCState());
EXPECT_EQ(0, test->GCCount());
test->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
EXPECT_TRUE(test->state()->IsSweepingInProgress());
EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
// Running the precise GC should simply finish the incremental marking idle GC
// (not run another GC).
EXPECT_EQ(0, test->GCCount());
test->state()->CompleteSweep();
EXPECT_EQ(1, test->GCCount());
}
TEST_F(ThreadStateSchedulingTest,
ScheduleIncrementalV8FollowupGCWhileIncrementalMarking) {
ThreadStateSchedulingTest* test = this;
test->StartIncrementalMarkingForIdleGC();
test->state()->ScheduleIncrementalGC(
BlinkGC::GCReason::kIncrementalV8FollowupGC);
// Scheduling a precise GC should not cancel incremental marking tasks.
EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
test->state()->GetGCState());
}
TEST_F(ThreadStateSchedulingTest, ScheduleIdleGCWhileGCForbidden) {
ThreadStateSchedulingTest* test = this;
test->state()->ScheduleIdleGC();
EXPECT_EQ(ThreadState::kIdleGCScheduled, test->state()->GetGCState());
ThreadState::GCForbiddenScope gc_forbidden_scope(test->state());
test->RunIdleGCTask();
// Starting an idle GC while GC is forbidden should reschedule it.
EXPECT_EQ(ThreadState::kIdleGCScheduled, test->state()->GetGCState());
}
TEST_F(ThreadStateSchedulingTest,
ScheduleIncrementalV8FollowupGCWhileGCForbidden) {
ThreadStateSchedulingTest* test = this;
EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
test->state()->ScheduleIncrementalGC(
BlinkGC::GCReason::kIncrementalV8FollowupGC);
EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
ThreadState::GCForbiddenScope gc_forbidden_scope(test->state());
test->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
// Starting an IncrementalV8FollowupGC while GC is forbidden should do
// nothing.
EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
EXPECT_EQ(0, GCCount());
}
TEST_F(ThreadStateSchedulingTest, RunIncrementalV8FollowupGC) {
ThreadStateSchedulingTest* test = this;
EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
test->state()->ScheduleIncrementalGC(
BlinkGC::GCReason::kIncrementalV8FollowupGC);
EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
test->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
test->state()->GetGCState());
}
} // namespace blink