Use idle time to make progress on scheduled compilation jobs
BUG=v8:5215 R=rmcilroy@chromium.org,marja@chromium.org,vogelheim@chromium.org Review-Url: https://codereview.chromium.org/2573493002 Cr-Commit-Position: refs/heads/master@{#41767}
This commit is contained in:
parent
c0d6939836
commit
692ba84f4f
@ -263,5 +263,36 @@ void CompilerDispatcherJob::ResetOnMainThread() {
|
||||
status_ = CompileJobStatus::kInitial;
|
||||
}
|
||||
|
||||
double CompilerDispatcherJob::EstimateRuntimeOfNextStepInMs() const {
|
||||
switch (status_) {
|
||||
case CompileJobStatus::kInitial:
|
||||
return tracer_->EstimatePrepareToParseInMs();
|
||||
|
||||
case CompileJobStatus::kReadyToParse:
|
||||
return tracer_->EstimateParseInMs(parse_info_->end_position() -
|
||||
parse_info_->start_position());
|
||||
|
||||
case CompileJobStatus::kParsed:
|
||||
return tracer_->EstimateFinalizeParsingInMs();
|
||||
|
||||
case CompileJobStatus::kReadyToAnalyse:
|
||||
return tracer_->EstimatePrepareToCompileInMs();
|
||||
|
||||
case CompileJobStatus::kReadyToCompile:
|
||||
return tracer_->EstimateCompileInMs(
|
||||
parse_info_->literal()->ast_node_count());
|
||||
|
||||
case CompileJobStatus::kCompiled:
|
||||
return tracer_->EstimateFinalizeCompilingInMs();
|
||||
|
||||
case CompileJobStatus::kFailed:
|
||||
case CompileJobStatus::kDone:
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -83,6 +83,9 @@ class V8_EXPORT_PRIVATE CompilerDispatcherJob {
|
||||
// Transition from any state to kInitial and free all resources.
|
||||
void ResetOnMainThread();
|
||||
|
||||
// Estimate how long the next step will take using the tracer.
|
||||
double EstimateRuntimeOfNextStepInMs() const;
|
||||
|
||||
private:
|
||||
FRIEND_TEST(CompilerDispatcherJobTest, ScopeChain);
|
||||
|
||||
|
@ -16,6 +16,8 @@ double MonotonicallyIncreasingTimeInMs() {
|
||||
static_cast<double>(base::Time::kMillisecondsPerSecond);
|
||||
}
|
||||
|
||||
const double kEstimatedRuntimeWithoutData = 1.0;
|
||||
|
||||
} // namespace
|
||||
|
||||
CompilerDispatcherTracer::Scope::Scope(CompilerDispatcherTracer* tracer,
|
||||
@ -129,22 +131,23 @@ double CompilerDispatcherTracer::EstimateParseInMs(size_t source_length) const {
|
||||
return Estimate(parse_events_, source_length);
|
||||
}
|
||||
|
||||
double CompilerDispatcherTracer::EstimateFinalizeParsingInMs() {
|
||||
double CompilerDispatcherTracer::EstimateFinalizeParsingInMs() const {
|
||||
base::LockGuard<base::Mutex> lock(&mutex_);
|
||||
return Average(finalize_parsing_events_);
|
||||
}
|
||||
|
||||
double CompilerDispatcherTracer::EstimatePrepareToCompileInMs() {
|
||||
double CompilerDispatcherTracer::EstimatePrepareToCompileInMs() const {
|
||||
base::LockGuard<base::Mutex> lock(&mutex_);
|
||||
return Average(prepare_compile_events_);
|
||||
}
|
||||
|
||||
double CompilerDispatcherTracer::EstimateCompileInMs(size_t ast_size_in_bytes) {
|
||||
double CompilerDispatcherTracer::EstimateCompileInMs(
|
||||
size_t ast_size_in_bytes) const {
|
||||
base::LockGuard<base::Mutex> lock(&mutex_);
|
||||
return Estimate(compile_events_, ast_size_in_bytes);
|
||||
}
|
||||
|
||||
double CompilerDispatcherTracer::EstimateFinalizeCompilingInMs() {
|
||||
double CompilerDispatcherTracer::EstimateFinalizeCompilingInMs() const {
|
||||
base::LockGuard<base::Mutex> lock(&mutex_);
|
||||
return Average(finalize_compiling_events_);
|
||||
}
|
||||
@ -158,7 +161,7 @@ double CompilerDispatcherTracer::Average(
|
||||
|
||||
double CompilerDispatcherTracer::Estimate(
|
||||
const base::RingBuffer<std::pair<size_t, double>>& buffer, size_t num) {
|
||||
if (buffer.Count() == 0) return 0.0;
|
||||
if (buffer.Count() == 0) return kEstimatedRuntimeWithoutData;
|
||||
std::pair<size_t, double> sum = buffer.Sum(
|
||||
[](std::pair<size_t, double> a, std::pair<size_t, double> b) {
|
||||
return std::make_pair(a.first + b.first, a.second + b.second);
|
||||
|
@ -69,10 +69,10 @@ class V8_EXPORT_PRIVATE CompilerDispatcherTracer {
|
||||
|
||||
double EstimatePrepareToParseInMs() const;
|
||||
double EstimateParseInMs(size_t source_length) const;
|
||||
double EstimateFinalizeParsingInMs();
|
||||
double EstimatePrepareToCompileInMs();
|
||||
double EstimateCompileInMs(size_t ast_size_in_bytes);
|
||||
double EstimateFinalizeCompilingInMs();
|
||||
double EstimateFinalizeParsingInMs() const;
|
||||
double EstimatePrepareToCompileInMs() const;
|
||||
double EstimateCompileInMs(size_t ast_size_in_bytes) const;
|
||||
double EstimateFinalizeCompilingInMs() const;
|
||||
|
||||
private:
|
||||
static double Average(const base::RingBuffer<double>& buffer);
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
#include "src/compiler-dispatcher/compiler-dispatcher.h"
|
||||
|
||||
#include "include/v8-platform.h"
|
||||
#include "include/v8.h"
|
||||
#include "src/base/platform/time.h"
|
||||
#include "src/cancelable-task.h"
|
||||
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
|
||||
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
|
||||
#include "src/objects-inl.h"
|
||||
@ -13,7 +17,12 @@ namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
bool DoNextStepOnMainThread(CompilerDispatcherJob* job) {
|
||||
enum class ExceptionHandling { kSwallow, kThrow };
|
||||
|
||||
bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job,
|
||||
ExceptionHandling exception_handling) {
|
||||
DCHECK(ThreadId::Current().Equals(isolate->thread_id()));
|
||||
v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
|
||||
switch (job->status()) {
|
||||
case CompileJobStatus::kInitial:
|
||||
job->PrepareToParseOnMainThread();
|
||||
@ -44,6 +53,11 @@ bool DoNextStepOnMainThread(CompilerDispatcherJob* job) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (exception_handling == ExceptionHandling::kThrow &&
|
||||
try_catch.HasCaught()) {
|
||||
DCHECK(job->status() == CompileJobStatus::kFailed);
|
||||
try_catch.ReThrow();
|
||||
}
|
||||
return job->status() != CompileJobStatus::kFailed;
|
||||
}
|
||||
|
||||
@ -52,14 +66,48 @@ bool IsFinished(CompilerDispatcherJob* job) {
|
||||
job->status() == CompileJobStatus::kFailed;
|
||||
}
|
||||
|
||||
// Theoretically we get 50ms of idle time max, however it's unlikely that
|
||||
// we'll get all of it so try to be a conservative.
|
||||
const double kMaxIdleTimeToExpectInMs = 40;
|
||||
|
||||
} // namespace
|
||||
|
||||
CompilerDispatcher::CompilerDispatcher(Isolate* isolate, size_t max_stack_size)
|
||||
: isolate_(isolate),
|
||||
max_stack_size_(max_stack_size),
|
||||
tracer_(new CompilerDispatcherTracer(isolate_)) {}
|
||||
class CompilerDispatcher::IdleTask : public CancelableIdleTask {
|
||||
public:
|
||||
IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher);
|
||||
~IdleTask() override;
|
||||
|
||||
CompilerDispatcher::~CompilerDispatcher() {}
|
||||
// CancelableIdleTask implementation.
|
||||
void RunInternal(double deadline_in_seconds) override;
|
||||
|
||||
private:
|
||||
CompilerDispatcher* dispatcher_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(IdleTask);
|
||||
};
|
||||
|
||||
CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate,
|
||||
CompilerDispatcher* dispatcher)
|
||||
: CancelableIdleTask(isolate), dispatcher_(dispatcher) {}
|
||||
|
||||
CompilerDispatcher::IdleTask::~IdleTask() {}
|
||||
|
||||
void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) {
|
||||
dispatcher_->DoIdleWork(deadline_in_seconds);
|
||||
}
|
||||
|
||||
CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
|
||||
size_t max_stack_size)
|
||||
: isolate_(isolate),
|
||||
platform_(platform),
|
||||
max_stack_size_(max_stack_size),
|
||||
tracer_(new CompilerDispatcherTracer(isolate_)),
|
||||
idle_task_scheduled_(false) {}
|
||||
|
||||
CompilerDispatcher::~CompilerDispatcher() {
|
||||
// To avoid crashing in unit tests due to unfished jobs.
|
||||
AbortAll(BlockingBehavior::kBlock);
|
||||
}
|
||||
|
||||
bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
|
||||
// We only handle functions (no eval / top-level code / wasm) that are
|
||||
@ -75,6 +123,7 @@ bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
|
||||
std::pair<int, int> key(Script::cast(function->script())->id(),
|
||||
function->function_literal_id());
|
||||
jobs_.insert(std::make_pair(key, std::move(job)));
|
||||
ScheduleIdleTaskIfNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -89,9 +138,11 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
|
||||
// TODO(jochen): Check if there's an in-flight background task working on this
|
||||
// job.
|
||||
while (!IsFinished(job->second.get())) {
|
||||
DoNextStepOnMainThread(job->second.get());
|
||||
DoNextStepOnMainThread(isolate_, job->second.get(),
|
||||
ExceptionHandling::kThrow);
|
||||
}
|
||||
bool result = job->second->status() != CompileJobStatus::kFailed;
|
||||
job->second->ResetOnMainThread();
|
||||
jobs_.erase(job);
|
||||
return result;
|
||||
}
|
||||
@ -104,6 +155,7 @@ void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function,
|
||||
|
||||
// TODO(jochen): Check if there's an in-flight background task working on this
|
||||
// job.
|
||||
job->second->ResetOnMainThread();
|
||||
jobs_.erase(job);
|
||||
}
|
||||
|
||||
@ -111,6 +163,9 @@ void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
|
||||
USE(blocking);
|
||||
// TODO(jochen): Check if there's an in-flight background task working on this
|
||||
// job.
|
||||
for (auto& kv : jobs_) {
|
||||
kv.second->ResetOnMainThread();
|
||||
}
|
||||
jobs_.clear();
|
||||
}
|
||||
|
||||
@ -126,5 +181,54 @@ CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
|
||||
return jobs_.end();
|
||||
}
|
||||
|
||||
void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
|
||||
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
|
||||
if (!platform_->IdleTasksEnabled(v8_isolate)) return;
|
||||
if (idle_task_scheduled_) return;
|
||||
if (jobs_.empty()) return;
|
||||
idle_task_scheduled_ = true;
|
||||
platform_->CallIdleOnForegroundThread(v8_isolate,
|
||||
new IdleTask(isolate_, this));
|
||||
}
|
||||
|
||||
void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
|
||||
idle_task_scheduled_ = false;
|
||||
|
||||
// Number of jobs that are unlikely to make progress during any idle callback
|
||||
// due to their estimated duration.
|
||||
size_t too_long_jobs = 0;
|
||||
|
||||
// Iterate over all available jobs & remaining time. For each job, decide
|
||||
// whether to 1) skip it (if it would take too long), 2) erase it (if it's
|
||||
// finished), or 3) make progress on it.
|
||||
double idle_time_in_seconds =
|
||||
deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
|
||||
for (auto job = jobs_.begin();
|
||||
job != jobs_.end() && idle_time_in_seconds > 0.0;
|
||||
idle_time_in_seconds =
|
||||
deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
|
||||
double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
|
||||
if (idle_time_in_seconds <
|
||||
(estimate_in_ms /
|
||||
static_cast<double>(base::Time::kMillisecondsPerSecond))) {
|
||||
// If there's not enough time left, try to estimate whether we would
|
||||
// have managed to finish the job in a large idle task to assess
|
||||
// whether we should ask for another idle callback.
|
||||
if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs;
|
||||
++job;
|
||||
} else if (IsFinished(job->second.get())) {
|
||||
job->second->ResetOnMainThread();
|
||||
job = jobs_.erase(job);
|
||||
break;
|
||||
} else {
|
||||
// Do one step, and keep processing the job (as we don't advance the
|
||||
// iterator).
|
||||
DoNextStepOnMainThread(isolate_, job->second.get(),
|
||||
ExceptionHandling::kSwallow);
|
||||
}
|
||||
}
|
||||
if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -11,8 +11,12 @@
|
||||
|
||||
#include "src/base/macros.h"
|
||||
#include "src/globals.h"
|
||||
#include "testing/gtest/include/gtest/gtest_prod.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
class Platform;
|
||||
|
||||
namespace internal {
|
||||
|
||||
class CompilerDispatcherJob;
|
||||
@ -23,11 +27,14 @@ class SharedFunctionInfo;
|
||||
template <typename T>
|
||||
class Handle;
|
||||
|
||||
// The CompilerDispatcher uses a combination of idle tasks and background tasks
|
||||
// to parse and compile lazily parsed functions.
|
||||
class V8_EXPORT_PRIVATE CompilerDispatcher {
|
||||
public:
|
||||
enum class BlockingBehavior { kBlock, kDontBlock };
|
||||
|
||||
CompilerDispatcher(Isolate* isolate, size_t max_stack_size);
|
||||
CompilerDispatcher(Isolate* isolate, Platform* platform,
|
||||
size_t max_stack_size);
|
||||
~CompilerDispatcher();
|
||||
|
||||
// Returns true if a job was enqueued.
|
||||
@ -47,15 +54,24 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
|
||||
void AbortAll(BlockingBehavior blocking);
|
||||
|
||||
private:
|
||||
FRIEND_TEST(CompilerDispatcherTest, IdleTaskSmallIdleTime);
|
||||
|
||||
typedef std::multimap<std::pair<int, int>,
|
||||
std::unique_ptr<CompilerDispatcherJob>>
|
||||
JobMap;
|
||||
class IdleTask;
|
||||
|
||||
JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const;
|
||||
void ScheduleIdleTaskIfNeeded();
|
||||
void DoIdleWork(double deadline_in_seconds);
|
||||
|
||||
Isolate* isolate_;
|
||||
Platform* platform_;
|
||||
size_t max_stack_size_;
|
||||
std::unique_ptr<CompilerDispatcherTracer> tracer_;
|
||||
|
||||
bool idle_task_scheduled_;
|
||||
|
||||
// Mapping from (script id, function literal id) to job. We use a multimap,
|
||||
// as script id is not necessarily unique.
|
||||
JobMap jobs_;
|
||||
|
@ -2518,7 +2518,8 @@ bool Isolate::Init(Deserializer* des) {
|
||||
cpu_profiler_ = new CpuProfiler(this);
|
||||
heap_profiler_ = new HeapProfiler(heap());
|
||||
interpreter_ = new interpreter::Interpreter(this);
|
||||
compiler_dispatcher_ = new CompilerDispatcher(this, FLAG_stack_size);
|
||||
compiler_dispatcher_ =
|
||||
new CompilerDispatcher(this, V8::GetCurrentPlatform(), FLAG_stack_size);
|
||||
|
||||
// Enable logging before setting up the heap
|
||||
logger_->SetUp(this);
|
||||
|
@ -8,16 +8,16 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
TEST(CompilerDispatcherTracerTest, EstimateZeroWithoutSamples) {
|
||||
TEST(CompilerDispatcherTracerTest, EstimateWithoutSamples) {
|
||||
CompilerDispatcherTracer tracer(nullptr);
|
||||
|
||||
EXPECT_EQ(0.0, tracer.EstimatePrepareToParseInMs());
|
||||
EXPECT_EQ(0.0, tracer.EstimateParseInMs(0));
|
||||
EXPECT_EQ(0.0, tracer.EstimateParseInMs(42));
|
||||
EXPECT_EQ(1.0, tracer.EstimateParseInMs(0));
|
||||
EXPECT_EQ(1.0, tracer.EstimateParseInMs(42));
|
||||
EXPECT_EQ(0.0, tracer.EstimateFinalizeParsingInMs());
|
||||
EXPECT_EQ(0.0, tracer.EstimatePrepareToCompileInMs());
|
||||
EXPECT_EQ(0.0, tracer.EstimateCompileInMs(0));
|
||||
EXPECT_EQ(0.0, tracer.EstimateCompileInMs(42));
|
||||
EXPECT_EQ(1.0, tracer.EstimateCompileInMs(0));
|
||||
EXPECT_EQ(1.0, tracer.EstimateCompileInMs(42));
|
||||
EXPECT_EQ(0.0, tracer.EstimateFinalizeCompilingInMs());
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ TEST(CompilerDispatcherTracerTest, Average) {
|
||||
TEST(CompilerDispatcherTracerTest, SizeBasedAverage) {
|
||||
CompilerDispatcherTracer tracer(nullptr);
|
||||
|
||||
EXPECT_EQ(0.0, tracer.EstimateParseInMs(100));
|
||||
EXPECT_EQ(1.0, tracer.EstimateParseInMs(100));
|
||||
|
||||
// All three samples parse 100 units/ms.
|
||||
tracer.RecordParse(1.0, 100);
|
||||
|
@ -2,9 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/compiler-dispatcher/compiler-dispatcher.h"
|
||||
|
||||
#include "include/v8-platform.h"
|
||||
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
|
||||
#include "src/flags.h"
|
||||
#include "src/handles.h"
|
||||
#include "src/objects-inl.h"
|
||||
@ -17,45 +18,190 @@ namespace internal {
|
||||
|
||||
typedef TestWithContext CompilerDispatcherTest;
|
||||
|
||||
namespace {
|
||||
|
||||
class MockPlatform : public v8::Platform {
|
||||
public:
|
||||
MockPlatform() : task_(nullptr), time_(0.0), time_step_(0.0) {}
|
||||
~MockPlatform() override = default;
|
||||
|
||||
void CallOnBackgroundThread(Task* task,
|
||||
ExpectedRuntime expected_runtime) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
|
||||
double delay_in_seconds) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CallIdleOnForegroundThread(v8::Isolate* isolate,
|
||||
IdleTask* task) override {
|
||||
task_ = task;
|
||||
}
|
||||
|
||||
bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; }
|
||||
|
||||
double MonotonicallyIncreasingTime() override {
|
||||
time_ += time_step_;
|
||||
return time_;
|
||||
}
|
||||
|
||||
void RunIdleTask(double deadline_in_seconds, double time_step) {
|
||||
ASSERT_TRUE(task_ != nullptr);
|
||||
time_step_ = time_step;
|
||||
IdleTask* task = task_;
|
||||
task_ = nullptr;
|
||||
task->Run(deadline_in_seconds);
|
||||
delete task;
|
||||
}
|
||||
|
||||
bool IdleTaskPending() const { return !!task_; }
|
||||
|
||||
private:
|
||||
IdleTask* task_;
|
||||
double time_;
|
||||
double time_step_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MockPlatform);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(CompilerDispatcherTest, Construct) {
|
||||
std::unique_ptr<CompilerDispatcher> dispatcher(
|
||||
new CompilerDispatcher(i_isolate(), FLAG_stack_size));
|
||||
MockPlatform platform;
|
||||
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
|
||||
}
|
||||
|
||||
TEST_F(CompilerDispatcherTest, IsEnqueued) {
|
||||
std::unique_ptr<CompilerDispatcher> dispatcher(
|
||||
new CompilerDispatcher(i_isolate(), FLAG_stack_size));
|
||||
MockPlatform platform;
|
||||
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
|
||||
|
||||
const char script[] =
|
||||
"function g() { var y = 1; function f(x) { return x * y }; return f; } "
|
||||
"function g() { var y = 1; function f1(x) { return x * y }; return f1; } "
|
||||
"g();";
|
||||
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
||||
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
|
||||
|
||||
ASSERT_FALSE(dispatcher->IsEnqueued(shared));
|
||||
ASSERT_TRUE(dispatcher->Enqueue(shared));
|
||||
ASSERT_TRUE(dispatcher->IsEnqueued(shared));
|
||||
dispatcher->Abort(shared, CompilerDispatcher::BlockingBehavior::kBlock);
|
||||
ASSERT_FALSE(dispatcher->IsEnqueued(shared));
|
||||
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
|
||||
ASSERT_TRUE(dispatcher.Enqueue(shared));
|
||||
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
|
||||
dispatcher.Abort(shared, CompilerDispatcher::BlockingBehavior::kBlock);
|
||||
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
|
||||
}
|
||||
|
||||
TEST_F(CompilerDispatcherTest, FinishNow) {
|
||||
std::unique_ptr<CompilerDispatcher> dispatcher(
|
||||
new CompilerDispatcher(i_isolate(), FLAG_stack_size));
|
||||
MockPlatform platform;
|
||||
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
|
||||
|
||||
const char script[] =
|
||||
"function g() { var y = 1; function f(x) { return x * y }; return f; } "
|
||||
"function g() { var y = 1; function f2(x) { return x * y }; return f2; } "
|
||||
"g();";
|
||||
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
||||
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
|
||||
|
||||
ASSERT_FALSE(shared->HasBaselineCode());
|
||||
ASSERT_TRUE(dispatcher->Enqueue(shared));
|
||||
ASSERT_TRUE(dispatcher->FinishNow(shared));
|
||||
ASSERT_TRUE(dispatcher.Enqueue(shared));
|
||||
ASSERT_TRUE(dispatcher.FinishNow(shared));
|
||||
// Finishing removes the SFI from the queue.
|
||||
ASSERT_FALSE(dispatcher->IsEnqueued(shared));
|
||||
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
|
||||
ASSERT_TRUE(shared->HasBaselineCode());
|
||||
}
|
||||
|
||||
TEST_F(CompilerDispatcherTest, IdleTask) {
|
||||
MockPlatform platform;
|
||||
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
|
||||
|
||||
const char script[] =
|
||||
"function g() { var y = 1; function f3(x) { return x * y }; return f3; } "
|
||||
"g();";
|
||||
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
||||
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
|
||||
|
||||
ASSERT_FALSE(platform.IdleTaskPending());
|
||||
ASSERT_TRUE(dispatcher.Enqueue(shared));
|
||||
ASSERT_TRUE(platform.IdleTaskPending());
|
||||
|
||||
// Since time doesn't progress on the MockPlatform, this is enough idle time
|
||||
// to finish compiling the function.
|
||||
platform.RunIdleTask(1000.0, 0.0);
|
||||
|
||||
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
|
||||
ASSERT_TRUE(shared->HasBaselineCode());
|
||||
}
|
||||
|
||||
TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
|
||||
MockPlatform platform;
|
||||
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
|
||||
|
||||
const char script[] =
|
||||
"function g() { var y = 1; function f4(x) { return x * y }; return f4; } "
|
||||
"g();";
|
||||
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
||||
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
|
||||
|
||||
ASSERT_FALSE(platform.IdleTaskPending());
|
||||
ASSERT_TRUE(dispatcher.Enqueue(shared));
|
||||
ASSERT_TRUE(platform.IdleTaskPending());
|
||||
|
||||
// The job should be scheduled for the main thread.
|
||||
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
|
||||
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
|
||||
CompileJobStatus::kInitial);
|
||||
|
||||
// Only grant a little idle time and have time advance beyond it in one step.
|
||||
platform.RunIdleTask(2.0, 1.0);
|
||||
|
||||
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
|
||||
ASSERT_FALSE(shared->HasBaselineCode());
|
||||
ASSERT_TRUE(platform.IdleTaskPending());
|
||||
|
||||
// The job should be still scheduled for the main thread, but ready for
|
||||
// parsing.
|
||||
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
|
||||
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
|
||||
CompileJobStatus::kReadyToParse);
|
||||
|
||||
// Only grant a lot of idle time and freeze time.
|
||||
platform.RunIdleTask(1000.0, 0.0);
|
||||
|
||||
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
|
||||
ASSERT_TRUE(shared->HasBaselineCode());
|
||||
ASSERT_FALSE(platform.IdleTaskPending());
|
||||
}
|
||||
|
||||
TEST_F(CompilerDispatcherTest, IdleTaskException) {
|
||||
MockPlatform platform;
|
||||
CompilerDispatcher dispatcher(i_isolate(), &platform, 50);
|
||||
|
||||
std::string script("function g() { function f5(x) { var a = ");
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
script += "'x' + ";
|
||||
}
|
||||
script += " 'x'; }; return f5; } g();";
|
||||
Handle<JSFunction> f =
|
||||
Handle<JSFunction>::cast(RunJS(isolate(), script.c_str()));
|
||||
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
|
||||
|
||||
ASSERT_FALSE(platform.IdleTaskPending());
|
||||
ASSERT_TRUE(dispatcher.Enqueue(shared));
|
||||
ASSERT_TRUE(platform.IdleTaskPending());
|
||||
|
||||
// Idle tasks shouldn't leave exceptions behind.
|
||||
v8::TryCatch try_catch(isolate());
|
||||
|
||||
// Since time doesn't progress on the MockPlatform, this is enough idle time
|
||||
// to finish compiling the function.
|
||||
platform.RunIdleTask(1000.0, 0.0);
|
||||
|
||||
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
|
||||
ASSERT_FALSE(shared->HasBaselineCode());
|
||||
ASSERT_FALSE(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user