blob: 59697a94a1fed1b2c695918ab45aaf3af1ec8c34 [file] [log] [blame]
// Copyright 2015 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 <stdlib.h>
#ifdef __linux__
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include <utility>
#include "src/v8.h"
#include "src/full-codegen/full-codegen.h"
#include "src/global-handles.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-utils.h"
using v8::IdleTask;
using v8::Task;
using v8::Isolate;
namespace v8 {
namespace internal {
class MockPlatform : public v8::Platform {
public:
explicit MockPlatform(v8::Platform* platform)
: platform_(platform), idle_task_(nullptr), delayed_task_(nullptr) {}
virtual ~MockPlatform() {
delete idle_task_;
delete delayed_task_;
}
void CallOnBackgroundThread(Task* task,
ExpectedRuntime expected_runtime) override {
platform_->CallOnBackgroundThread(task, expected_runtime);
}
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
platform_->CallOnForegroundThread(isolate, task);
}
void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
double delay_in_seconds) override {
if (delayed_task_ != nullptr) {
delete delayed_task_;
}
delayed_task_ = task;
}
double MonotonicallyIncreasingTime() override {
return platform_->MonotonicallyIncreasingTime();
}
void CallIdleOnForegroundThread(v8::Isolate* isolate,
IdleTask* task) override {
CHECK(nullptr == idle_task_);
idle_task_ = task;
}
bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; }
bool PendingIdleTask() { return idle_task_ != nullptr; }
void PerformIdleTask(double idle_time_in_seconds) {
IdleTask* task = idle_task_;
idle_task_ = nullptr;
task->Run(MonotonicallyIncreasingTime() + idle_time_in_seconds);
delete task;
}
bool PendingDelayedTask() { return delayed_task_ != nullptr; }
void PerformDelayedTask() {
Task* task = delayed_task_;
delayed_task_ = nullptr;
task->Run();
delete task;
}
uint64_t AddTraceEvent(char phase, const uint8_t* categoryEnabledFlag,
const char* name, const char* scope, uint64_t id,
uint64_t bind_id, int numArgs, const char** argNames,
const uint8_t* argTypes, const uint64_t* argValues,
unsigned int flags) override {
return 0;
}
void UpdateTraceEventDuration(const uint8_t* categoryEnabledFlag,
const char* name, uint64_t handle) override {}
const uint8_t* GetCategoryGroupEnabled(const char* name) override {
static uint8_t no = 0;
return &no;
}
const char* GetCategoryGroupName(
const uint8_t* categoryEnabledFlag) override {
static const char* dummy = "dummy";
return dummy;
}
private:
v8::Platform* platform_;
IdleTask* idle_task_;
Task* delayed_task_;
};
TEST(IncrementalMarkingUsingIdleTasks) {
if (!i::FLAG_incremental_marking) return;
CcTest::InitializeVM();
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockPlatform platform(old_platform);
i::V8::SetPlatformForTesting(&platform);
i::heap::SimulateFullSpace(CcTest::heap()->old_space());
i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
marking->Stop();
marking->Start();
CHECK(platform.PendingIdleTask());
const double kLongIdleTimeInSeconds = 1;
const double kShortIdleTimeInSeconds = 0.010;
const int kShortStepCount = 10;
for (int i = 0; i < kShortStepCount && platform.PendingIdleTask(); i++) {
platform.PerformIdleTask(kShortIdleTimeInSeconds);
}
while (platform.PendingIdleTask()) {
platform.PerformIdleTask(kLongIdleTimeInSeconds);
}
CHECK(marking->IsStopped());
i::V8::SetPlatformForTesting(old_platform);
}
TEST(IncrementalMarkingUsingIdleTasksAfterGC) {
if (!i::FLAG_incremental_marking) return;
CcTest::InitializeVM();
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockPlatform platform(old_platform);
i::V8::SetPlatformForTesting(&platform);
i::heap::SimulateFullSpace(CcTest::heap()->old_space());
CcTest::heap()->CollectAllGarbage();
i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
marking->Stop();
marking->Start();
CHECK(platform.PendingIdleTask());
const double kLongIdleTimeInSeconds = 1;
const double kShortIdleTimeInSeconds = 0.010;
const int kShortStepCount = 10;
for (int i = 0; i < kShortStepCount && platform.PendingIdleTask(); i++) {
platform.PerformIdleTask(kShortIdleTimeInSeconds);
}
while (platform.PendingIdleTask()) {
platform.PerformIdleTask(kLongIdleTimeInSeconds);
}
CHECK(marking->IsStopped());
i::V8::SetPlatformForTesting(old_platform);
}
TEST(IncrementalMarkingUsingDelayedTasks) {
if (!i::FLAG_incremental_marking) return;
CcTest::InitializeVM();
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockPlatform platform(old_platform);
i::V8::SetPlatformForTesting(&platform);
i::heap::SimulateFullSpace(CcTest::heap()->old_space());
i::IncrementalMarking* marking = CcTest::heap()->incremental_marking();
marking->Stop();
marking->Start();
CHECK(platform.PendingIdleTask());
// The delayed task should be a no-op if the idle task makes progress.
const int kIgnoredDelayedTaskStepCount = 1000;
for (int i = 0; i < kIgnoredDelayedTaskStepCount; i++) {
// Dummy idle task progress.
marking->incremental_marking_job()->NotifyIdleTaskProgress();
CHECK(platform.PendingDelayedTask());
platform.PerformDelayedTask();
}
// Once we stop notifying idle task progress, the delayed tasks
// should finish marking.
while (!marking->IsStopped() && platform.PendingDelayedTask()) {
platform.PerformDelayedTask();
}
// There could be pending delayed task from memory reducer after GC finishes.
CHECK(marking->IsStopped());
i::V8::SetPlatformForTesting(old_platform);
}
} // namespace internal
} // namespace v8