[heap] Introduce getters for MemoryReducer::State

This CL hides the ctor of the MemoryReducer::State class and only
provides factory methods for creating states. This simplifies creation
of states and makes it impossible to misuse the API.

Direct field accesses are also replaced with invocations of their
corresponding getter methods. The getter method will check whether
the current state is allowed to access that field.

Bug: v8:13653
Change-Id: I252a6d75d0ddb4813b16a706061ad1951cfa35ea
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4181026
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85426}
This commit is contained in:
Dominik Inführ 2023-01-20 09:37:08 +01:00 committed by V8 LUCI CQ
parent 417ce7ef46
commit d31e52879b
4 changed files with 210 additions and 196 deletions

View File

@ -25,7 +25,7 @@ MemoryReducer::MemoryReducer(Heap* heap)
: heap_(heap),
taskrunner_(V8::GetCurrentPlatform()->GetForegroundTaskRunner(
reinterpret_cast<v8::Isolate*>(heap->isolate()))),
state_(kDone, 0, 0.0, 0.0, 0),
state_(State::CreateUninitialized()),
js_calls_counter_(0),
js_calls_sample_time_ms_(0.0) {}
@ -66,19 +66,19 @@ void MemoryReducer::TimerTask::RunInternal() {
void MemoryReducer::NotifyTimer(const Event& event) {
DCHECK_EQ(kTimer, event.type);
DCHECK_EQ(kWait, state_.action);
DCHECK_EQ(kWait, state_.id());
state_ = Step(state_, event);
if (state_.action == kRun) {
if (state_.id() == kRun) {
DCHECK(heap()->incremental_marking()->IsStopped());
DCHECK(v8_flags.incremental_marking);
if (v8_flags.trace_gc_verbose) {
heap()->isolate()->PrintWithTimestamp("Memory reducer: started GC #%d\n",
state_.started_gcs);
state_.started_gcs());
}
heap()->StartIdleIncrementalMarking(
GarbageCollectionReason::kMemoryReducer,
kGCCallbackFlagCollectAllExternalMemory);
} else if (state_.action == kWait) {
} else if (state_.id() == kWait) {
if (!heap()->incremental_marking()->IsStopped() &&
heap()->ShouldOptimizeForMemoryUsage()) {
// Make progress with pending incremental marking if memory usage has
@ -87,11 +87,11 @@ void MemoryReducer::NotifyTimer(const Event& event) {
heap()->incremental_marking()->AdvanceAndFinalizeIfComplete();
}
// Re-schedule the timer.
ScheduleTimer(state_.next_gc_start_ms - event.time_ms);
ScheduleTimer(state_.next_gc_start_ms() - event.time_ms);
if (v8_flags.trace_gc_verbose) {
heap()->isolate()->PrintWithTimestamp(
"Memory reducer: waiting for %.f ms\n",
state_.next_gc_start_ms - event.time_ms);
state_.next_gc_start_ms() - event.time_ms);
}
}
}
@ -110,17 +110,17 @@ void MemoryReducer::NotifyMarkCompact(size_t committed_memory_before) {
heap()->HasHighFragmentation(),
false,
false};
const Action old_action = state_.action;
const Id old_action = state_.id();
state_ = Step(state_, event);
if (old_action != kWait && state_.action == kWait) {
if (old_action != kWait && state_.id() == kWait) {
// If we are transitioning to the WAIT state, start the timer.
ScheduleTimer(state_.next_gc_start_ms - event.time_ms);
ScheduleTimer(state_.next_gc_start_ms() - event.time_ms);
}
if (old_action == kRun) {
if (v8_flags.trace_gc_verbose) {
heap()->isolate()->PrintWithTimestamp(
"Memory reducer: finished GC #%d (%s)\n", state_.started_gcs,
state_.action == kWait ? "will do more" : "done");
"Memory reducer: finished GC #%d (%s)\n", state_.started_gcs(),
state_.id() == kWait ? "will do more" : "done");
}
}
}
@ -132,17 +132,17 @@ void MemoryReducer::NotifyPossibleGarbage() {
0,
false,
false};
const Action old_action = state_.action;
const Id old_action = state_.id();
state_ = Step(state_, event);
if (old_action != kWait && state_.action == kWait) {
if (old_action != kWait && state_.id() == kWait) {
// If we are transitioning to the WAIT state, start the timer.
ScheduleTimer(state_.next_gc_start_ms - event.time_ms);
ScheduleTimer(state_.next_gc_start_ms() - event.time_ms);
}
}
bool MemoryReducer::WatchdogGC(const State& state, const Event& event) {
return state.last_gc_time_ms != 0 &&
event.time_ms > state.last_gc_time_ms + kWatchdogDelayMs;
return state.last_gc_time_ms() != 0 &&
event.time_ms > state.last_gc_time_ms() + kWatchdogDelayMs;
}
@ -150,63 +150,64 @@ bool MemoryReducer::WatchdogGC(const State& state, const Event& event) {
MemoryReducer::State MemoryReducer::Step(const State& state,
const Event& event) {
if (!v8_flags.incremental_marking || !v8_flags.memory_reducer) {
return State(kDone, 0, 0, state.last_gc_time_ms, 0);
return State::CreateUninitialized();
}
switch (state.action) {
switch (state.id()) {
case kDone:
if (event.type == kTimer) {
return state;
} else if (event.type == kMarkCompact) {
if (event.committed_memory <
std::max(
static_cast<size_t>(state.committed_memory_at_last_run *
static_cast<size_t>(state.committed_memory_at_last_run() *
kCommittedMemoryFactor),
state.committed_memory_at_last_run + kCommittedMemoryDelta)) {
state.committed_memory_at_last_run() + kCommittedMemoryDelta)) {
return state;
} else {
return State(kWait, 0, event.time_ms + kLongDelayMs, event.time_ms,
0);
return State::CreateWait(0, event.time_ms + kLongDelayMs,
event.time_ms);
}
} else {
DCHECK_EQ(kPossibleGarbage, event.type);
return State(kWait, 0,
event.time_ms + v8_flags.gc_memory_reducer_start_delay_ms,
state.last_gc_time_ms, 0);
return State::CreateWait(
0, event.time_ms + v8_flags.gc_memory_reducer_start_delay_ms,
state.last_gc_time_ms());
}
case kWait:
switch (event.type) {
case kPossibleGarbage:
return state;
case kTimer:
if (state.started_gcs >= kMaxNumberOfGCs) {
return State(kDone, kMaxNumberOfGCs, 0.0, state.last_gc_time_ms,
event.committed_memory);
if (state.started_gcs() >= kMaxNumberOfGCs) {
return State::CreateDone(state.last_gc_time_ms(),
event.committed_memory);
} else if (event.can_start_incremental_gc &&
(event.should_start_incremental_gc ||
WatchdogGC(state, event))) {
if (state.next_gc_start_ms <= event.time_ms) {
return State(kRun, state.started_gcs + 1, 0.0,
state.last_gc_time_ms, 0);
if (state.next_gc_start_ms() <= event.time_ms) {
return State::CreateRun(state.started_gcs() + 1);
} else {
return state;
}
} else {
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs,
state.last_gc_time_ms, 0);
return State::CreateWait(state.started_gcs(),
event.time_ms + kLongDelayMs,
state.last_gc_time_ms());
}
case kMarkCompact:
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs,
event.time_ms, 0);
return State::CreateWait(state.started_gcs(),
event.time_ms + kLongDelayMs, event.time_ms);
}
case kRun:
if (event.type == kMarkCompact) {
if (state.started_gcs < kMaxNumberOfGCs &&
(event.next_gc_likely_to_collect_more || state.started_gcs == 1)) {
return State(kWait, state.started_gcs, event.time_ms + kShortDelayMs,
event.time_ms, 0);
if (state.started_gcs() < kMaxNumberOfGCs &&
(event.next_gc_likely_to_collect_more ||
state.started_gcs() == 1)) {
return State::CreateWait(state.started_gcs(),
event.time_ms + kShortDelayMs,
event.time_ms);
} else {
return State(kDone, kMaxNumberOfGCs, 0.0, event.time_ms,
event.committed_memory);
return State::CreateDone(event.time_ms, event.committed_memory);
}
} else {
return state;
@ -224,7 +225,7 @@ void MemoryReducer::ScheduleTimer(double delay_ms) {
(delay_ms + kSlackMs) / 1000.0);
}
void MemoryReducer::TearDown() { state_ = State(kDone, 0, 0, 0.0, 0); }
void MemoryReducer::TearDown() { state_ = State::CreateUninitialized(); }
} // namespace internal
} // namespace v8

View File

@ -86,21 +86,61 @@ class Heap;
// long_delay_ms, short_delay_ms, and watchdog_delay_ms are constants.
class V8_EXPORT_PRIVATE MemoryReducer {
public:
enum Action { kDone, kWait, kRun };
enum Id { kDone, kWait, kRun };
struct State {
State(Action action, int started_gcs, double next_gc_start_ms,
class State {
public:
static State CreateUninitialized() { return {kDone, 0, 0, 0, 0}; }
static State CreateDone(double last_gc_time_ms, size_t committed_memory) {
return {kDone, kMaxNumberOfGCs, 0, last_gc_time_ms, committed_memory};
}
static State CreateWait(int started_gcs, double next_gc_time_ms,
double last_gc_time_ms) {
return {kWait, started_gcs, next_gc_time_ms, last_gc_time_ms, 0};
}
static State CreateRun(int started_gcs) {
return {kRun, started_gcs, 0, 0, 0};
}
Id id() const { return id_; }
int started_gcs() const {
DCHECK(id() == kWait || id() == kRun || id() == kDone);
return started_gcs_;
}
double next_gc_start_ms() const {
DCHECK_EQ(id(), kWait);
return next_gc_start_ms_;
}
double last_gc_time_ms() const {
DCHECK(id() == kWait || id() == kDone);
return last_gc_time_ms_;
}
size_t committed_memory_at_last_run() const {
DCHECK_EQ(id(), kDone);
return committed_memory_at_last_run_;
}
private:
State(Id action, int started_gcs, double next_gc_start_ms,
double last_gc_time_ms, size_t committed_memory_at_last_run)
: action(action),
started_gcs(started_gcs),
next_gc_start_ms(next_gc_start_ms),
last_gc_time_ms(last_gc_time_ms),
committed_memory_at_last_run(committed_memory_at_last_run) {}
Action action;
int started_gcs;
double next_gc_start_ms;
double last_gc_time_ms;
size_t committed_memory_at_last_run;
: id_(action),
started_gcs_(started_gcs),
next_gc_start_ms_(next_gc_start_ms),
last_gc_time_ms_(last_gc_time_ms),
committed_memory_at_last_run_(committed_memory_at_last_run) {}
Id id_;
int started_gcs_;
double next_gc_start_ms_;
double last_gc_time_ms_;
size_t committed_memory_at_last_run_;
};
enum EventType { kTimer, kMarkCompact, kPossibleGarbage };
@ -140,7 +180,7 @@ class V8_EXPORT_PRIVATE MemoryReducer {
Heap* heap() { return heap_; }
bool ShouldGrowHeapSlowly() {
return state_.action == kDone && state_.started_gcs > 0;
return state_.id() == kDone && state_.started_gcs() > 0;
}
private:

View File

@ -6982,7 +6982,7 @@ HEAP_TEST(MemoryReducerActivationForSmallHeaps) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
Heap* heap = isolate->heap();
CHECK_EQ(heap->memory_reducer()->state_.action, MemoryReducer::Action::kDone);
CHECK_EQ(heap->memory_reducer()->state_.id(), MemoryReducer::kDone);
HandleScope scope(isolate);
const size_t kActivationThreshold = 1 * MB;
size_t initial_capacity = heap->OldGenerationCapacity();
@ -6990,7 +6990,7 @@ HEAP_TEST(MemoryReducerActivationForSmallHeaps) {
initial_capacity + kActivationThreshold) {
isolate->factory()->NewFixedArray(1 * KB, AllocationType::kOld);
}
CHECK_EQ(heap->memory_reducer()->state_.action, MemoryReducer::Action::kWait);
CHECK_EQ(heap->memory_reducer()->state_.id(), MemoryReducer::kWait);
}
TEST(AllocateExternalBackingStore) {

View File

@ -11,26 +11,6 @@
namespace v8 {
namespace internal {
MemoryReducer::State DoneState() {
return MemoryReducer::State(MemoryReducer::kDone, 0, 0.0, 1.0, 0);
}
MemoryReducer::State DoneState(size_t committed_memory) {
return MemoryReducer::State(MemoryReducer::kDone, 0, 0.0, 1.0,
committed_memory);
}
MemoryReducer::State WaitState(int started_gcs, double next_gc_start_ms) {
return MemoryReducer::State(MemoryReducer::kWait, started_gcs,
next_gc_start_ms, 1.0, 0);
}
MemoryReducer::State RunState(int started_gcs, double next_gc_start_ms) {
return MemoryReducer::State(MemoryReducer::kRun, started_gcs,
next_gc_start_ms, 1.0, 0);
}
MemoryReducer::Event MarkCompactEvent(double time_ms,
bool next_gc_likely_to_collect_more,
size_t committed_memory) {
@ -88,248 +68,241 @@ MemoryReducer::Event PossibleGarbageEvent(double time_ms) {
TEST(MemoryReducer, FromDoneToDone) {
MemoryReducer::State state0(DoneState()), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateDone(1.0, 0)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(state0, TimerEventLowAllocationRate(0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
state1 = MemoryReducer::Step(
state0,
MarkCompactEventGarbageLeft(0, MemoryReducer::kCommittedMemoryDelta - 1));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
state0 = DoneState(1000 * MB);
state0 = MemoryReducer::State::CreateDone(1, 1000 * MB);
state1 = MemoryReducer::Step(
state0, MarkCompactEventGarbageLeft(
0, static_cast<size_t>(
1000 * MB * MemoryReducer::kCommittedMemoryFactor) -
1));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
}
TEST(MemoryReducer, FromDoneToWait) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(DoneState()), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateDone(1.0, 0)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(
state0,
MarkCompactEventGarbageLeft(2, MemoryReducer::kCommittedMemoryDelta));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(v8_flags.gc_memory_reducer_start_delay_ms + 2,
state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2, state1.last_gc_time_ms);
state1.next_gc_start_ms());
EXPECT_EQ(0, state1.started_gcs());
EXPECT_EQ(2, state1.last_gc_time_ms());
state1 = MemoryReducer::Step(
state0,
MarkCompactEventNoGarbageLeft(2, MemoryReducer::kCommittedMemoryDelta));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(v8_flags.gc_memory_reducer_start_delay_ms + 2,
state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2, state1.last_gc_time_ms);
state1.next_gc_start_ms());
EXPECT_EQ(0, state1.started_gcs());
EXPECT_EQ(2, state1.last_gc_time_ms());
state1 = MemoryReducer::Step(state0, PossibleGarbageEvent(0));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(v8_flags.gc_memory_reducer_start_delay_ms, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(v8_flags.gc_memory_reducer_start_delay_ms,
state1.next_gc_start_ms());
EXPECT_EQ(0, state1.started_gcs());
EXPECT_EQ(state0.last_gc_time_ms(), state1.last_gc_time_ms());
state0 = DoneState(1000 * MB);
state0 = MemoryReducer::State::CreateDone(1, 1000 * MB);
state1 = MemoryReducer::Step(
state0, MarkCompactEventGarbageLeft(
2, static_cast<size_t>(
1000 * MB * MemoryReducer::kCommittedMemoryFactor)));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(v8_flags.gc_memory_reducer_start_delay_ms + 2,
state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2, state1.last_gc_time_ms);
state1.next_gc_start_ms());
EXPECT_EQ(0, state1.started_gcs());
EXPECT_EQ(2, state1.last_gc_time_ms());
}
TEST(MemoryReducer, FromWaitToWait) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(WaitState(2, 1000.0)), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateWait(2, 1000.0, 1)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(state0, PossibleGarbageEvent(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(state0.next_gc_start_ms(), state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(
state0, TimerEventLowAllocationRate(state0.next_gc_start_ms - 1));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
state0, TimerEventLowAllocationRate(state0.next_gc_start_ms() - 1));
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(state0.next_gc_start_ms(), state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(state0, MarkCompactEventGarbageLeft(2000, 0));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
EXPECT_EQ(2000, state1.last_gc_time_ms());
state1 = MemoryReducer::Step(state0, MarkCompactEventNoGarbageLeft(2000, 0));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
EXPECT_EQ(2000, state1.last_gc_time_ms());
state0 = MemoryReducer::State::CreateWait(2, 1000.0, 0);
state0.last_gc_time_ms = 0;
state1 = MemoryReducer::Step(
state0,
TimerEventHighAllocationRate(MemoryReducer::kWatchdogDelayMs + 1));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(MemoryReducer::kWatchdogDelayMs + 1 + MemoryReducer::kLongDelayMs,
state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
EXPECT_EQ(state0.last_gc_time_ms(), state1.last_gc_time_ms());
state0.last_gc_time_ms = 1;
state0 = MemoryReducer::State::CreateWait(2, 1000.0, 1);
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
EXPECT_EQ(state0.last_gc_time_ms(), state1.last_gc_time_ms());
}
TEST(MemoryReducer, FromWaitToRun) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(WaitState(0, 1000.0)), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateWait(0, 1000.0, 1)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(
state0, TimerEventLowAllocationRate(state0.next_gc_start_ms + 1));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs + 1, state1.started_gcs);
state0, TimerEventLowAllocationRate(state0.next_gc_start_ms() + 1));
EXPECT_EQ(MemoryReducer::kRun, state1.id());
EXPECT_EQ(state0.started_gcs() + 1, state1.started_gcs());
state1 = MemoryReducer::Step(
state0,
TimerEventHighAllocationRate(MemoryReducer::kWatchdogDelayMs + 2));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs + 1, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kRun, state1.id());
EXPECT_EQ(state0.started_gcs() + 1, state1.started_gcs());
}
TEST(MemoryReducer, FromWaitToDone) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(WaitState(2, 0.0)), state1(DoneState());
state0.started_gcs = MemoryReducer::kMaxNumberOfGCs;
MemoryReducer::State state0(
MemoryReducer::State::CreateWait(MemoryReducer::kMaxNumberOfGCs, 0.0, 1)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(state0, TimerEventLowAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs());
EXPECT_EQ(state0.last_gc_time_ms(), state1.last_gc_time_ms());
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs());
EXPECT_EQ(state0.last_gc_time_ms(), state1.last_gc_time_ms());
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs());
EXPECT_EQ(state0.last_gc_time_ms(), state1.last_gc_time_ms());
}
TEST(MemoryReducer, FromRunToRun) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(RunState(1, 0.0)), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateRun(1)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(state0, TimerEventLowAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kRun, state1.id());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kRun, state1.id());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kRun, state1.id());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
state1 = MemoryReducer::Step(state0, PossibleGarbageEvent(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kRun, state1.id());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
}
TEST(MemoryReducer, FromRunToDone) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(RunState(2, 0.0)), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateRun(2)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(state0, MarkCompactEventNoGarbageLeft(2000, 0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
EXPECT_EQ(MemoryReducer::kMaxNumberOfGCs, state1.started_gcs());
EXPECT_EQ(2000, state1.last_gc_time_ms());
state0.started_gcs = MemoryReducer::kMaxNumberOfGCs;
state0 = MemoryReducer::State::CreateRun(MemoryReducer::kMaxNumberOfGCs);
state1 = MemoryReducer::Step(state0, MarkCompactEventGarbageLeft(2000, 0));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(2000, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kDone, state1.id());
EXPECT_EQ(state1.started_gcs(), state1.started_gcs());
}
TEST(MemoryReducer, FromRunToWait) {
if (!v8_flags.incremental_marking) return;
MemoryReducer::State state0(RunState(2, 0.0)), state1(DoneState());
MemoryReducer::State state0(MemoryReducer::State::CreateRun(2)),
state1(MemoryReducer::State::CreateDone(1.0, 0));
state1 = MemoryReducer::Step(state0, MarkCompactEventGarbageLeft(2000, 0));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kShortDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kShortDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
EXPECT_EQ(2000, state1.last_gc_time_ms());
state0.started_gcs = 1;
state0 = MemoryReducer::State::CreateRun(1);
state1 = MemoryReducer::Step(state0, MarkCompactEventNoGarbageLeft(2000, 0));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kShortDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
EXPECT_EQ(MemoryReducer::kWait, state1.id());
EXPECT_EQ(2000 + MemoryReducer::kShortDelayMs, state1.next_gc_start_ms());
EXPECT_EQ(state0.started_gcs(), state1.started_gcs());
EXPECT_EQ(2000, state1.last_gc_time_ms());
}
} // namespace internal