Commit 493c894a authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[Compiler] Remove CompilerDispatcherJob and use BackgroundCompileTask directly

Simplify the logic in the CompilerDispatcher to use BackgroundCompileTasks
directly, rather than having a (now unecessary) CompilerDispatcherJob
abstraction. In the process, the CompilerDispatcherTracer is removed, and the
idle task logic is simplified finalize already compiled jobs until the
idle task deadline.

BUG=v8:8238, v8:8041

Change-Id: I1ea2366f959b6951de222d62fde80725b3cc70ff
Reviewed-on: https://chromium-review.googlesource.com/c/1260123
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56473}
parent e3a42cfd
......@@ -1669,16 +1669,10 @@ v8_source_set("v8_base") {
"src/compilation-cache.h",
"src/compilation-statistics.cc",
"src/compilation-statistics.h",
"src/compiler-dispatcher/compiler-dispatcher-job.cc",
"src/compiler-dispatcher/compiler-dispatcher-job.h",
"src/compiler-dispatcher/compiler-dispatcher-tracer.cc",
"src/compiler-dispatcher/compiler-dispatcher-tracer.h",
"src/compiler-dispatcher/compiler-dispatcher.cc",
"src/compiler-dispatcher/compiler-dispatcher.h",
"src/compiler-dispatcher/optimizing-compile-dispatcher.cc",
"src/compiler-dispatcher/optimizing-compile-dispatcher.h",
"src/compiler-dispatcher/unoptimized-compile-job.cc",
"src/compiler-dispatcher/unoptimized-compile-job.h",
"src/compiler.cc",
"src/compiler.h",
"src/compiler/access-builder.cc",
......
// Copyright 2016 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/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/compiler-dispatcher/unoptimized-compile-job.h"
namespace v8 {
namespace internal {
const UnoptimizedCompileJob* CompilerDispatcherJob::AsUnoptimizedCompileJob()
const {
DCHECK_EQ(type(), Type::kUnoptimizedCompile);
return static_cast<const UnoptimizedCompileJob*>(this);
}
} // namespace internal
} // namespace v8
// Copyright 2016 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.
#ifndef V8_COMPILER_DISPATCHER_COMPILER_DISPATCHER_JOB_H_
#define V8_COMPILER_DISPATCHER_COMPILER_DISPATCHER_JOB_H_
#include "src/contexts.h"
#include "src/handles.h"
namespace v8 {
namespace internal {
class SharedFunctionInfo;
class UnoptimizedCompileJob;
class V8_EXPORT_PRIVATE CompilerDispatcherJob {
public:
enum class Type { kUnoptimizedCompile };
enum class Status {
kInitial,
kReadyToFinalize,
kDone,
kFailed,
};
CompilerDispatcherJob(Type type) : type_(type), status_(Status::kInitial) {}
virtual ~CompilerDispatcherJob() = default;
Type type() const { return type_; }
// Returns the current status of the compile
Status status() const { return status_; }
// Returns true if this CompilerDispatcherJob has finished (either with a
// success or a failure).
bool IsFinished() const {
return status() == Status::kDone || status() == Status::kFailed;
}
// Returns true if this CompilerDispatcherJob has failed.
bool IsFailed() const { return status() == Status::kFailed; }
// Return true if the next step can be run on any thread.
bool NextStepCanRunOnAnyThread() const {
return status() == Status::kInitial;
}
// Casts to implementations.
const UnoptimizedCompileJob* AsUnoptimizedCompileJob() const;
// Transition from kInitial to kReadyToFinalize.
virtual void Compile(bool on_background_thread) = 0;
// Transition from kReadyToFinalize to kDone (or kFailed). Must only be
// invoked on the main thread.
virtual void FinalizeOnMainThread(Isolate* isolate,
Handle<SharedFunctionInfo> shared) = 0;
// Free all resources. Must only be invoked on the main thread.
virtual void ResetOnMainThread(Isolate* isolate) = 0;
// Estimate how long the next step will take using the tracer.
virtual double EstimateRuntimeOfNextStepInMs() const = 0;
protected:
void set_status(Status status) { status_ = status; }
private:
Type type_;
Status status_;
};
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_DISPATCHER_COMPILER_DISPATCHER_JOB_H_
// Copyright 2016 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/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/isolate.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
namespace {
double MonotonicallyIncreasingTimeInMs() {
return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() *
static_cast<double>(base::Time::kMillisecondsPerSecond);
}
const double kEstimatedRuntimeWithoutData = 1.0;
} // namespace
CompilerDispatcherTracer::Scope::Scope(CompilerDispatcherTracer* tracer,
ScopeID scope_id, size_t num)
: tracer_(tracer), scope_id_(scope_id), num_(num) {
start_time_ = MonotonicallyIncreasingTimeInMs();
}
CompilerDispatcherTracer::Scope::~Scope() {
double elapsed = MonotonicallyIncreasingTimeInMs() - start_time_;
switch (scope_id_) {
case ScopeID::kPrepare:
tracer_->RecordPrepare(elapsed);
break;
case ScopeID::kCompile:
tracer_->RecordCompile(elapsed, num_);
break;
case ScopeID::kFinalize:
tracer_->RecordFinalize(elapsed);
break;
}
}
// static
const char* CompilerDispatcherTracer::Scope::Name(ScopeID scope_id) {
switch (scope_id) {
case ScopeID::kPrepare:
return "V8.BackgroundCompile_Prepare";
case ScopeID::kCompile:
return "V8.BackgroundCompile_Compile";
case ScopeID::kFinalize:
return "V8.BackgroundCompile_Finalize";
}
UNREACHABLE();
}
CompilerDispatcherTracer::CompilerDispatcherTracer(Isolate* isolate)
: runtime_call_stats_(nullptr) {
// isolate might be nullptr during unittests.
if (isolate) {
runtime_call_stats_ = isolate->counters()->runtime_call_stats();
}
}
CompilerDispatcherTracer::~CompilerDispatcherTracer() = default;
void CompilerDispatcherTracer::RecordPrepare(double duration_ms) {
base::LockGuard<base::Mutex> lock(&mutex_);
prepare_events_.Push(duration_ms);
}
void CompilerDispatcherTracer::RecordCompile(double duration_ms,
size_t source_length) {
base::LockGuard<base::Mutex> lock(&mutex_);
compile_events_.Push(std::make_pair(source_length, duration_ms));
}
void CompilerDispatcherTracer::RecordFinalize(double duration_ms) {
base::LockGuard<base::Mutex> lock(&mutex_);
finalize_events_.Push(duration_ms);
}
double CompilerDispatcherTracer::EstimatePrepareInMs() const {
base::LockGuard<base::Mutex> lock(&mutex_);
return Average(prepare_events_);
}
double CompilerDispatcherTracer::EstimateCompileInMs(
size_t source_length) const {
base::LockGuard<base::Mutex> lock(&mutex_);
return Estimate(compile_events_, source_length);
}
double CompilerDispatcherTracer::EstimateFinalizeInMs() const {
base::LockGuard<base::Mutex> lock(&mutex_);
return Average(finalize_events_);
}
void CompilerDispatcherTracer::DumpStatistics() const {
PrintF(
"CompilerDispatcherTracer: "
"prepare=%.2lfms compiling=%.2lfms/kb finalize=%.2lfms\n",
EstimatePrepareInMs(), EstimateCompileInMs(1 * KB),
EstimateFinalizeInMs());
}
double CompilerDispatcherTracer::Average(
const base::RingBuffer<double>& buffer) {
if (buffer.Count() == 0) return 0.0;
double sum = buffer.Sum([](double a, double b) { return a + b; }, 0.0);
return sum / buffer.Count();
}
double CompilerDispatcherTracer::Estimate(
const base::RingBuffer<std::pair<size_t, double>>& buffer, size_t num) {
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);
},
std::make_pair(0, 0.0));
return num * (sum.second / sum.first);
}
} // namespace internal
} // namespace v8
// Copyright 2016 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.
#ifndef V8_COMPILER_DISPATCHER_COMPILER_DISPATCHER_TRACER_H_
#define V8_COMPILER_DISPATCHER_COMPILER_DISPATCHER_TRACER_H_
#include <utility>
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "src/base/ring-buffer.h"
#include "src/counters.h"
#include "src/globals.h"
namespace v8 {
namespace internal {
class Isolate;
class RuntimeCallStats;
#define COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(tracer, scope_id, num) \
CompilerDispatcherTracer::ScopeID tracer_scope_id( \
CompilerDispatcherTracer::ScopeID::scope_id); \
CompilerDispatcherTracer::Scope trace_scope(tracer, tracer_scope_id, num); \
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), \
CompilerDispatcherTracer::Scope::Name(tracer_scope_id))
#define COMPILER_DISPATCHER_TRACE_SCOPE(tracer, scope_id) \
COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(tracer, scope_id, 0)
class V8_EXPORT_PRIVATE CompilerDispatcherTracer {
public:
enum class ScopeID { kPrepare, kCompile, kFinalize };
class Scope {
public:
Scope(CompilerDispatcherTracer* tracer, ScopeID scope_id, size_t num = 0);
~Scope();
static const char* Name(ScopeID scoped_id);
private:
CompilerDispatcherTracer* tracer_;
ScopeID scope_id_;
size_t num_;
double start_time_;
DISALLOW_COPY_AND_ASSIGN(Scope);
};
explicit CompilerDispatcherTracer(Isolate* isolate);
~CompilerDispatcherTracer();
void RecordPrepare(double duration_ms);
void RecordCompile(double duration_ms, size_t source_length);
void RecordFinalize(double duration_ms);
double EstimatePrepareInMs() const;
double EstimateCompileInMs(size_t source_length) const;
double EstimateFinalizeInMs() const;
void DumpStatistics() const;
private:
static double Average(const base::RingBuffer<double>& buffer);
static double Estimate(
const base::RingBuffer<std::pair<size_t, double>>& buffer, size_t num);
mutable base::Mutex mutex_;
base::RingBuffer<double> prepare_events_;
base::RingBuffer<std::pair<size_t, double>> compile_events_;
base::RingBuffer<double> finalize_events_;
RuntimeCallStats* runtime_call_stats_;
DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherTracer);
};
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_DISPATCHER_COMPILER_DISPATCHER_TRACER_H_
......@@ -8,38 +8,20 @@
#include "src/base/platform/time.h"
#include "src/base/template-utils.h"
#include "src/cancelable-task.h"
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/compiler-dispatcher/unoptimized-compile-job.h"
#include "src/compiler.h"
#include "src/flags.h"
#include "src/global-handles.h"
#include "src/objects-inl.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parser.h"
namespace v8 {
namespace internal {
namespace {
CompilerDispatcher::Job::Job(BackgroundCompileTask* task_arg)
: task(task_arg), has_run(false) {}
enum class ExceptionHandling { kSwallow, kThrow };
void FinalizeJobOnMainThread(Isolate* isolate, CompilerDispatcherJob* job,
Handle<SharedFunctionInfo> shared,
ExceptionHandling exception_handling) {
DCHECK(ThreadId::Current().Equals(isolate->thread_id()));
DCHECK_EQ(job->status(), CompilerDispatcherJob::Status::kReadyToFinalize);
job->FinalizeOnMainThread(isolate, shared);
DCHECK_EQ(job->IsFailed(), isolate->has_pending_exception());
if (job->IsFailed() && exception_handling == ExceptionHandling::kSwallow) {
isolate->clear_pending_exception();
}
}
// 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::Job::~Job() = default;
CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
size_t max_stack_size)
......@@ -54,7 +36,6 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
platform_(platform),
max_stack_size_(max_stack_size),
trace_compiler_dispatcher_(FLAG_trace_compiler_dispatcher),
tracer_(new CompilerDispatcherTracer(isolate_)),
task_manager_(new CancelableTaskManager()),
next_job_id_(0),
shared_to_unoptimized_job_id_(isolate->heap()),
......@@ -103,10 +84,10 @@ base::Optional<CompilerDispatcher::JobId> CompilerDispatcher::Enqueue(
if (!CanEnqueue()) return base::nullopt;
std::unique_ptr<CompilerDispatcherJob> job(new UnoptimizedCompileJob(
tracer_.get(), allocator_, outer_parse_info, function_name,
function_literal, worker_thread_runtime_call_stats_,
background_compile_timer_, max_stack_size_));
std::unique_ptr<Job> job = base::make_unique<Job>(new BackgroundCompileTask(
allocator_, outer_parse_info, function_name, function_literal,
worker_thread_runtime_call_stats_, background_compile_timer_,
static_cast<int>(max_stack_size_)));
JobMap::const_iterator it = InsertJob(std::move(job));
JobId id = it->first;
if (trace_compiler_dispatcher_) {
......@@ -114,10 +95,13 @@ base::Optional<CompilerDispatcher::JobId> CompilerDispatcher::Enqueue(
id, function_literal->function_literal_id());
}
// Post a idle task and a background worker task to perform the compilation
// either on the worker thread or during idle time (whichever is first).
ConsiderJobForBackgroundProcessing(it->second.get());
ScheduleIdleTaskIfNeeded();
// Post a a background worker task to perform the compilation on the worker
// thread.
{
base::LockGuard<base::Mutex> lock(&mutex_);
pending_background_jobs_.insert(it->second.get());
}
ScheduleMoreWorkerTasksIfNeeded();
return base::make_optional(id);
}
......@@ -131,10 +115,10 @@ bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
bool CompilerDispatcher::IsEnqueued(JobId job_id) const {
return jobs_.find(job_id) != jobs_.end();
}
void CompilerDispatcher::RegisterSharedFunctionInfo(
JobId job_id, SharedFunctionInfo* function) {
DCHECK_NE(jobs_.find(job_id), jobs_.end());
DCHECK_EQ(job_id_to_shared_.find(job_id), job_id_to_shared_.end());
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: registering ");
......@@ -147,15 +131,25 @@ void CompilerDispatcher::RegisterSharedFunctionInfo(
isolate_->global_handles()->Create(function);
// Register mapping.
job_id_to_shared_.insert(std::make_pair(job_id, function_handle));
auto job_it = jobs_.find(job_id);
DCHECK_NE(job_it, jobs_.end());
Job* job = job_it->second.get();
shared_to_unoptimized_job_id_.Set(function_handle, job_id);
// Schedule an idle task to finalize job if it is ready.
ScheduleIdleTaskIfNeeded();
bool is_ready_to_finalize;
{
base::LockGuard<base::Mutex> lock(&mutex_);
job->function = function_handle;
is_ready_to_finalize = job->IsReadyToFinalize(lock);
}
// TODO(rmcilroy): Move idle task posting into locked block above.
if (is_ready_to_finalize) {
ScheduleIdleTaskIfNeeded();
}
}
void CompilerDispatcher::WaitForJobIfRunningOnBackground(
CompilerDispatcherJob* job) {
void CompilerDispatcher::WaitForJobIfRunningOnBackground(Job* job) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompilerDispatcherWaitForBackgroundJob");
RuntimeCallTimerScope runtimeTimer(
......@@ -188,28 +182,21 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
JobMap::const_iterator it = GetJobFor(function);
CHECK(it != jobs_.end());
CompilerDispatcherJob* job = it->second.get();
Job* job = it->second.get();
WaitForJobIfRunningOnBackground(job);
while (!job->IsFinished()) {
switch (job->status()) {
case CompilerDispatcherJob::Status::kInitial:
job->Compile(false);
break;
case CompilerDispatcherJob::Status::kReadyToFinalize: {
FinalizeJobOnMainThread(isolate_, job, function,
ExceptionHandling::kThrow);
break;
}
case CompilerDispatcherJob::Status::kFailed:
case CompilerDispatcherJob::Status::kDone:
UNREACHABLE();
}
if (!job->has_run) {
job->task->Run();
job->has_run = true;
}
DCHECK_EQ(job->IsFailed(), isolate_->has_pending_exception());
DCHECK(job->IsFinished());
bool result = !job->IsFailed();
DCHECK(job->IsReadyToFinalize(&mutex_));
bool success = Compiler::FinalizeBackgroundCompileTask(
job->task.get(), function, isolate_, Compiler::KEEP_EXCEPTION);
DCHECK_NE(success, isolate_->has_pending_exception());
RemoveJob(it);
return result;
return success;
}
void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
......@@ -221,7 +208,6 @@ void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: aborted job %zu\n", it.first);
}
it.second->ResetOnMainThread(isolate_);
}
jobs_.clear();
shared_to_unoptimized_job_id_.Clear();
......@@ -339,16 +325,6 @@ void CompilerDispatcher::ScheduleAbortTask() {
task_manager_.get(), [this] { AbortInactiveJobs(); }));
}
void CompilerDispatcher::ConsiderJobForBackgroundProcessing(
CompilerDispatcherJob* job) {
if (!job->NextStepCanRunOnAnyThread()) return;
{
base::LockGuard<base::Mutex> lock(&mutex_);
pending_background_jobs_.insert(job);
}
ScheduleMoreWorkerTasksIfNeeded();
}
void CompilerDispatcher::ScheduleMoreWorkerTasksIfNeeded() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompilerDispatcherScheduleMoreWorkerTasksIfNeeded");
......@@ -368,7 +344,7 @@ void CompilerDispatcher::DoBackgroundWork() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompilerDispatcherDoBackgroundWork");
for (;;) {
CompilerDispatcherJob* job = nullptr;
Job* job = nullptr;
{
base::LockGuard<base::Mutex> lock(&mutex_);
if (!pending_background_jobs_.empty()) {
......@@ -389,23 +365,27 @@ void CompilerDispatcher::DoBackgroundWork() {
PrintF("CompilerDispatcher: doing background work\n");
}
DCHECK(job->NextStepCanRunOnAnyThread());
DCHECK_EQ(job->status(), CompilerDispatcherJob::Status::kInitial);
job->Compile(true);
// Unconditionally schedule an idle task, as all background steps have to be
// followed by a main thread step.
ScheduleIdleTaskFromAnyThread();
job->task->Run();
bool is_ready_to_finalize;
{
base::LockGuard<base::Mutex> lock(&mutex_);
running_background_jobs_.erase(job);
job->has_run = true;
is_ready_to_finalize = job->IsReadyToFinalize(lock);
if (main_thread_blocking_on_job_ == job) {
main_thread_blocking_on_job_ = nullptr;
main_thread_blocking_signal_.NotifyOne();
}
}
// Schedule an idle task to finalize the compilation on the main thread if
// the job has a shared function info registered.
// TODO(rmcilroy) Move this into the locked section above.
if (is_ready_to_finalize) {
ScheduleIdleTaskFromAnyThread();
}
}
{
......@@ -438,102 +418,43 @@ void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
return;
}
// Number of jobs that are unlikely to make progress during any idle callback
// due to their estimated duration.
size_t jobs_unlikely_to_progress = 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 if possible.
double idle_time_in_seconds =
deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: received %0.1lfms of idle time\n",
idle_time_in_seconds *
(deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) *
static_cast<double>(base::Time::kMillisecondsPerSecond));
}
for (auto job = jobs_.cbegin();
job != jobs_.cend() && idle_time_in_seconds > 0.0;
idle_time_in_seconds =
deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
// Don't work on jobs that are being worked on by background tasks.
// Similarly, remove jobs we work on from the set of available background
// jobs.
std::unique_ptr<base::LockGuard<base::Mutex>> lock(
new base::LockGuard<base::Mutex>(&mutex_));
if (running_background_jobs_.find(job->second.get()) !=
running_background_jobs_.end()) {
++job;
continue;
}
DCHECK(!job->second->IsFinished());
auto it = pending_background_jobs_.find(job->second.get());
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.
// TODO(rmcilroy): Consider running the job anyway when we have a long
// idle time since this would probably be the best time to run.
if (estimate_in_ms > kMaxIdleTimeToExpectInMs)
++jobs_unlikely_to_progress;
if (it == pending_background_jobs_.end()) {
lock.reset();
ConsiderJobForBackgroundProcessing(job->second.get());
}
++job;
} else if (job->second->status() ==
CompilerDispatcherJob::Status::kInitial) {
if (it != pending_background_jobs_.end()) {
pending_background_jobs_.erase(it);
}
lock.reset();
job->second->Compile(false);
// Don't update job so we can immediately finalize it on the next loop.
} else {
DCHECK_EQ(job->second->status(),
CompilerDispatcherJob::Status::kReadyToFinalize);
DCHECK(it == pending_background_jobs_.end());
lock.reset();
auto shared_it = job_id_to_shared_.find(job->first);
if (shared_it != job_id_to_shared_.end()) {
Handle<SharedFunctionInfo> shared = shared_it->second;
FinalizeJobOnMainThread(isolate_, job->second.get(), shared,
ExceptionHandling::kSwallow);
DCHECK(job->second->IsFinished());
job = RemoveJob(job);
} else {
// If we can't step the job yet, go to the next job.
++jobs_unlikely_to_progress;
++job;
while (deadline_in_seconds > platform_->MonotonicallyIncreasingTime()) {
// Find a job which is pending finalization and has a shared function info
CompilerDispatcher::JobMap::const_iterator it;
{
base::LockGuard<base::Mutex> lock(&mutex_);
for (it = jobs_.cbegin(); it != jobs_.cend(); ++it) {
if (it->second->IsReadyToFinalize(lock)) break;
}
// Since we hold the lock here, we can be sure no jobs have become ready
// for finalization while we looped through the list.
if (it == jobs_.cend()) return;
DCHECK(it->second->IsReadyToFinalize(lock));
DCHECK_EQ(running_background_jobs_.find(it->second.get()),
running_background_jobs_.end());
DCHECK_EQ(pending_background_jobs_.find(it->second.get()),
pending_background_jobs_.end());
}
}
if (jobs_.size() > jobs_unlikely_to_progress) ScheduleIdleTaskIfNeeded();
}
CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::RemoveIfFinished(
JobMap::const_iterator job) {
if (!job->second->IsFinished()) {
return job;
}
if (trace_compiler_dispatcher_) {
bool result = !job->second->IsFailed();
PrintF("CompilerDispatcher: finished working on job %zu: %s\n", job->first,
result ? "success" : "failure");
tracer_->DumpStatistics();
Job* job = it->second.get();
Compiler::FinalizeBackgroundCompileTask(
job->task.get(), job->function.ToHandleChecked(), isolate_,
Compiler::CLEAR_EXCEPTION);
RemoveJob(it);
}
return RemoveJob(job);
// We didn't return above so there still might be jobs to finalize.
ScheduleIdleTaskIfNeeded();
}
CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::InsertJob(
std::unique_ptr<CompilerDispatcherJob> job) {
std::unique_ptr<Job> job) {
bool added;
JobMap::const_iterator it;
std::tie(it, added) =
......@@ -544,23 +465,18 @@ CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::InsertJob(
CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::RemoveJob(
CompilerDispatcher::JobMap::const_iterator it) {
CompilerDispatcherJob* job = it->second.get();
// Delete SFI associated with job if its been registered.
auto shared_it = job_id_to_shared_.find(it->first);
if (shared_it != job_id_to_shared_.end()) {
Handle<SharedFunctionInfo> shared = shared_it->second;
Job* job = it->second.get();
JobId deleted_id;
shared_to_unoptimized_job_id_.Delete(shared, &deleted_id);
DCHECK_EQ(it->first, deleted_id);
DCHECK_EQ(running_background_jobs_.find(job), running_background_jobs_.end());
DCHECK_EQ(pending_background_jobs_.find(job), pending_background_jobs_.end());
job_id_to_shared_.erase(shared_it);
GlobalHandles::Destroy(Handle<Object>::cast(shared).location());
// Delete SFI associated with job if its been registered.
Handle<SharedFunctionInfo> function;
if (job->function.ToHandle(&function)) {
GlobalHandles::Destroy(Handle<Object>::cast(function).location());
}
job->ResetOnMainThread(isolate_);
// Delete job.
it = jobs_.erase(it);
if (jobs_.empty()) {
base::LockGuard<base::Mutex> lock(&mutex_);
......
......@@ -19,6 +19,7 @@
#include "src/base/platform/semaphore.h"
#include "src/globals.h"
#include "src/identity-map.h"
#include "src/maybe-handles.h"
#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
namespace v8 {
......@@ -30,8 +31,8 @@ namespace internal {
class AstRawString;
class AstValueFactory;
class BackgroundCompileTask;
class CancelableTaskManager;
class CompilerDispatcherJob;
class UnoptimizedCompileJob;
class CompilerDispatcherTracer;
class DeferredHandles;
......@@ -111,43 +112,51 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
bool is_isolate_locked);
private:
FRIEND_TEST(CompilerDispatcherTest, EnqueueJob);
FRIEND_TEST(CompilerDispatcherTest, EnqueueWithoutSFI);
FRIEND_TEST(CompilerDispatcherTest, EnqueueAndStep);
FRIEND_TEST(CompilerDispatcherTest, EnqueueAndStepWithoutSFI);
FRIEND_TEST(CompilerDispatcherTest, EnqueueAndStepTwice);
FRIEND_TEST(CompilerDispatcherTest, EnqueueParsed);
FRIEND_TEST(CompilerDispatcherTest, EnqueueAndStepParsed);
FRIEND_TEST(CompilerDispatcherTest, IdleTaskNoIdleTime);
FRIEND_TEST(CompilerDispatcherTest, IdleTaskSmallIdleTime);
FRIEND_TEST(CompilerDispatcherTest, CompileOnBackgroundThread);
FRIEND_TEST(CompilerDispatcherTest, FinishNowWithWorkerTask);
FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllPendingWorkerTask);
FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllRunningWorkerTask);
FRIEND_TEST(CompilerDispatcherTest, FinishNowDuringAbortAll);
FRIEND_TEST(CompilerDispatcherTest, CompileMultipleOnBackgroundThread);
typedef std::map<JobId, std::unique_ptr<CompilerDispatcherJob>> JobMap;
typedef std::map<JobId, Handle<SharedFunctionInfo>> JobIdToSharedMap;
typedef IdentityMap<JobId, FreeStoreAllocationPolicy> SharedToJobIdMap;
class AbortTask;
class WorkerTask;
class IdleTask;
struct Job {
explicit Job(BackgroundCompileTask* task_arg);
~Job();
bool IsReadyToFinalize(const base::LockGuard<base::Mutex>&) {
return has_run && !function.is_null();
}
bool IsReadyToFinalize(base::Mutex* mutex) {
base::LockGuard<base::Mutex> lock(mutex);
return IsReadyToFinalize(lock);
}
std::unique_ptr<BackgroundCompileTask> task;
MaybeHandle<SharedFunctionInfo> function;
bool has_run;
};
typedef std::map<JobId, std::unique_ptr<Job>> JobMap;
typedef IdentityMap<JobId, FreeStoreAllocationPolicy> SharedToJobIdMap;
bool CanEnqueue();
void WaitForJobIfRunningOnBackground(CompilerDispatcherJob* job);
void WaitForJobIfRunningOnBackground(Job* job);
void AbortInactiveJobs();
JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const;
void ConsiderJobForBackgroundProcessing(CompilerDispatcherJob* job);
void ScheduleMoreWorkerTasksIfNeeded();
void ScheduleIdleTaskFromAnyThread();
void ScheduleIdleTaskIfNeeded();
void ScheduleAbortTask();
void DoBackgroundWork();
void DoIdleWork(double deadline_in_seconds);
// Returns job if not removed otherwise iterator following the removed job.
JobMap::const_iterator RemoveIfFinished(JobMap::const_iterator job);
// Returns iterator to the inserted job.
JobMap::const_iterator InsertJob(std::unique_ptr<CompilerDispatcherJob> job);
JobMap::const_iterator InsertJob(std::unique_ptr<Job> job);
// Returns iterator following the removed job.
JobMap::const_iterator RemoveJob(JobMap::const_iterator job);
......@@ -162,8 +171,6 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// Copy of FLAG_trace_compiler_dispatcher to allow for access from any thread.
bool trace_compiler_dispatcher_;
std::unique_ptr<CompilerDispatcherTracer> tracer_;
std::unique_ptr<CancelableTaskManager> task_manager_;
// Id for next job to be added
......@@ -172,9 +179,6 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// Mapping from job_id to job.
JobMap jobs_;
// Mapping from job_id to SharedFunctionInfo.
JobIdToSharedMap job_id_to_shared_;
// Mapping from SharedFunctionInfo to the corresponding unoptimized
// compilation's JobId;
SharedToJobIdMap shared_to_unoptimized_job_id_;
......@@ -193,16 +197,15 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// Number of scheduled or running WorkerTask objects.
int num_worker_tasks_;
// The set of CompilerDispatcherJobs that can be advanced on any thread.
std::unordered_set<CompilerDispatcherJob*> pending_background_jobs_;
// The set of jobs that can be run on a background thread.
std::unordered_set<Job*> pending_background_jobs_;
// The set of CompilerDispatcherJobs currently processed on background
// threads.
std::unordered_set<CompilerDispatcherJob*> running_background_jobs_;
// The set of jobs currently being run on background threads.
std::unordered_set<Job*> running_background_jobs_;
// If not nullptr, then the main thread waits for the task processing
// this job, and blocks on the ConditionVariable main_thread_blocking_signal_.
CompilerDispatcherJob* main_thread_blocking_on_job_;
Job* main_thread_blocking_on_job_;
base::ConditionVariable main_thread_blocking_signal_;
// Test support.
......
// Copyright 2016 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/compiler-dispatcher/unoptimized-compile-job.h"
#include "src/assert-scope.h"
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/compiler.h"
#include "src/flags.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parser.h"
#include "src/parsing/scanner-character-streams.h"
#include "src/unicode-cache.h"
#include "src/unoptimized-compilation-info.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
UnoptimizedCompileJob::UnoptimizedCompileJob(
CompilerDispatcherTracer* tracer, AccountingAllocator* allocator,
const ParseInfo* outer_parse_info, const AstRawString* function_name,
const FunctionLiteral* function_literal,
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats,
TimedHistogram* timer, size_t max_stack_size)
: CompilerDispatcherJob(Type::kUnoptimizedCompile),
tracer_(tracer),
task_(new BackgroundCompileTask(allocator, outer_parse_info,
function_name, function_literal,
worker_thread_runtime_stats, timer,
static_cast<int>(max_stack_size))) {}
UnoptimizedCompileJob::~UnoptimizedCompileJob() {
DCHECK(status() == Status::kInitial || status() == Status::kDone);
}
void UnoptimizedCompileJob::Compile(bool on_background_thread) {
DCHECK_EQ(status(), Status::kInitial);
COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
tracer_, kCompile,
task_->info()->end_position() - task_->info()->start_position());
task_->Run();
set_status(Status::kReadyToFinalize);
}
void UnoptimizedCompileJob::FinalizeOnMainThread(
Isolate* isolate, Handle<SharedFunctionInfo> shared) {
DCHECK_EQ(ThreadId::Current().ToInteger(), isolate->thread_id().ToInteger());
DCHECK_EQ(status(), Status::kReadyToFinalize);
COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalize);
bool succeeded = Compiler::FinalizeBackgroundCompileTask(
task_.get(), shared, isolate, Compiler::KEEP_EXCEPTION);
ResetDataOnMainThread(isolate);
set_status(succeeded ? Status::kDone : Status::kFailed);
}
void UnoptimizedCompileJob::ResetDataOnMainThread(Isolate* isolate) {
DCHECK_EQ(ThreadId::Current().ToInteger(), isolate->thread_id().ToInteger());
task_.reset();
}
void UnoptimizedCompileJob::ResetOnMainThread(Isolate* isolate) {
ResetDataOnMainThread(isolate);
set_status(Status::kInitial);
}
double UnoptimizedCompileJob::EstimateRuntimeOfNextStepInMs() const {
switch (status()) {
case Status::kInitial:
return tracer_->EstimateCompileInMs(task_->info()->end_position() -
task_->info()->start_position());
case Status::kReadyToFinalize:
// TODO(rmcilroy): Pass size of bytecode to tracer to get better estimate.
return tracer_->EstimateFinalizeInMs();
case Status::kFailed:
case Status::kDone:
return 0.0;
}
UNREACHABLE();
}
} // namespace internal
} // namespace v8
// Copyright 2016 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.
#ifndef V8_COMPILER_DISPATCHER_UNOPTIMIZED_COMPILE_JOB_H_
#define V8_COMPILER_DISPATCHER_UNOPTIMIZED_COMPILE_JOB_H_
#include <memory>
#include "include/v8.h"
#include "src/base/macros.h"
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/globals.h"
namespace v8 {
namespace internal {
class AccountingAllocator;
class AstRawString;
class AstValueFactory;
class AstStringConstants;
class BackgroundCompileTask;
class CompilerDispatcherTracer;
class DeferredHandles;
class FunctionLiteral;
class Isolate;
class ParseInfo;
class Parser;
class SharedFunctionInfo;
class String;
class TimedHistogram;
class UnicodeCache;
class UnoptimizedCompilationJob;
class WorkerThreadRuntimeCallStats;
// TODO(rmcilroy): Remove this class entirely and just have CompilerDispatcher
// manage BackgroundCompileTasks.
class V8_EXPORT_PRIVATE UnoptimizedCompileJob : public CompilerDispatcherJob {
public:
// Creates a UnoptimizedCompileJob in the initial state.
UnoptimizedCompileJob(
CompilerDispatcherTracer* tracer, AccountingAllocator* allocator,
const ParseInfo* outer_parse_info, const AstRawString* function_name,
const FunctionLiteral* function_literal,
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats,
TimedHistogram* timer, size_t max_stack_size);
~UnoptimizedCompileJob() override;
// CompilerDispatcherJob implementation.
void Compile(bool on_background_thread) override;
void FinalizeOnMainThread(Isolate* isolate,
Handle<SharedFunctionInfo> shared) override;
void ResetOnMainThread(Isolate* isolate) override;
double EstimateRuntimeOfNextStepInMs() const override;
private:
friend class CompilerDispatcherTest;
friend class UnoptimizedCompileJobTest;
void ResetDataOnMainThread(Isolate* isolate);
CompilerDispatcherTracer* tracer_;
std::unique_ptr<BackgroundCompileTask> task_;
DISALLOW_COPY_AND_ASSIGN(UnoptimizedCompileJob);
};
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_DISPATCHER_UNOPTIMIZED_COMPILE_JOB_H_
......@@ -945,6 +945,8 @@ BackgroundCompileTask::BackgroundCompileTask(
}
}
BackgroundCompileTask::~BackgroundCompileTask() = default;
namespace {
// A scope object that ensures a parse info's runtime call stats, stack limit
......
......@@ -316,13 +316,14 @@ class OptimizedCompilationJob : public CompilationJob {
const char* compiler_name_;
};
class BackgroundCompileTask {
class V8_EXPORT_PRIVATE BackgroundCompileTask {
public:
// Creates a new task that when run will parse and compile the streamed
// script associated with |data| and can be finalized with
// Compiler::GetSharedFunctionInfoForStreamedScript.
// Note: does not take ownership of |data|.
BackgroundCompileTask(ScriptStreamingData* data, Isolate* isolate);
~BackgroundCompileTask();
// Creates a new task that when run will parse and compile the
// |function_literal| and can be finalized with
......
......@@ -55,6 +55,7 @@ v8_source_set("unittests_sources") {
"asmjs/asm-scanner-unittest.cc",
"asmjs/asm-types-unittest.cc",
"asmjs/switch-logic-unittest.cc",
"background-compile-task-unittest.cc",
"base/address-region-unittest.cc",
"base/atomic-utils-unittest.cc",
"base/bits-unittest.cc",
......@@ -83,10 +84,8 @@ v8_source_set("unittests_sources") {
"char-predicates-unittest.cc",
"code-stub-assembler-unittest.cc",
"code-stub-assembler-unittest.h",
"compiler-dispatcher/compiler-dispatcher-tracer-unittest.cc",
"compiler-dispatcher/compiler-dispatcher-unittest.cc",
"compiler-dispatcher/optimizing-compile-dispatcher-unittest.cc",
"compiler-dispatcher/unoptimized-compile-job-unittest.cc",
"compiler/branch-elimination-unittest.cc",
"compiler/bytecode-analysis-unittest.cc",
"compiler/checkpoint-elimination-unittest.cc",
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Copyright 2018 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.
......@@ -10,12 +10,11 @@
#include "src/ast/scopes.h"
#include "src/base/platform/semaphore.h"
#include "src/base/template-utils.h"
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/compiler-dispatcher/unoptimized-compile-job.h"
#include "src/compiler.h"
#include "src/flags.h"
#include "src/isolate-inl.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parser.h"
#include "src/parsing/preparsed-scope-data.h"
#include "src/v8.h"
#include "test/unittests/test-helpers.h"
......@@ -25,14 +24,12 @@
namespace v8 {
namespace internal {
class UnoptimizedCompileJobTest : public TestWithNativeContext {
class BackgroundCompileTaskTest : public TestWithNativeContext {
public:
UnoptimizedCompileJobTest()
: tracer_(isolate()), allocator_(isolate()->allocator()) {}
~UnoptimizedCompileJobTest() override = default;
BackgroundCompileTaskTest() : allocator_(isolate()->allocator()) {}
~BackgroundCompileTaskTest() override = default;
AccountingAllocator* allocator() { return allocator_; }
CompilerDispatcherTracer* tracer() { return &tracer_; }
static void SetUpTestCase() {
CHECK_NULL(save_flags_);
......@@ -47,7 +44,7 @@ class UnoptimizedCompileJobTest : public TestWithNativeContext {
save_flags_ = nullptr;
}
UnoptimizedCompileJob* NewUnoptimizedCompileJob(
BackgroundCompileTask* NewBackgroundCompileTask(
Isolate* isolate, Handle<SharedFunctionInfo> shared,
size_t stack_size = FLAG_stack_size) {
std::unique_ptr<ParseInfo> outer_parse_info =
......@@ -74,74 +71,45 @@ class UnoptimizedCompileJobTest : public TestWithNativeContext {
FunctionLiteral::kShouldEagerCompile, shared->StartPosition(), true,
shared->FunctionLiteralId(isolate), nullptr);
return new UnoptimizedCompileJob(
tracer(), allocator(), outer_parse_info.get(), function_name,
function_literal,
return new BackgroundCompileTask(
allocator(), outer_parse_info.get(), function_name, function_literal,
isolate->counters()->worker_thread_runtime_call_stats(),
isolate->counters()->compile_function_on_background(), FLAG_stack_size);
}
private:
CompilerDispatcherTracer tracer_;
AccountingAllocator* allocator_;
static SaveFlags* save_flags_;
DISALLOW_COPY_AND_ASSIGN(UnoptimizedCompileJobTest);
DISALLOW_COPY_AND_ASSIGN(BackgroundCompileTaskTest);
};
SaveFlags* UnoptimizedCompileJobTest::save_flags_ = nullptr;
SaveFlags* BackgroundCompileTaskTest::save_flags_ = nullptr;
#define ASSERT_JOB_STATUS(STATUS, JOB) ASSERT_EQ(STATUS, JOB->status())
TEST_F(UnoptimizedCompileJobTest, Construct) {
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(isolate(), nullptr);
ASSERT_FALSE(shared->is_compiled());
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
}
TEST_F(UnoptimizedCompileJobTest, StateTransitions) {
TEST_F(BackgroundCompileTaskTest, Construct) {
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(isolate(), nullptr);
ASSERT_FALSE(shared->is_compiled());
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
job->Compile(false);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kReadyToFinalize, job);
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared));
}
TEST_F(UnoptimizedCompileJobTest, SyntaxError) {
TEST_F(BackgroundCompileTaskTest, SyntaxError) {
test::ScriptResource* script = new test::ScriptResource("^^^", strlen("^^^"));
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(isolate(), script);
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared));
job->Compile(false);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kReadyToFinalize, job);
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_TRUE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kFailed, job);
task->Run();
ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
ASSERT_TRUE(isolate()->has_pending_exception());
isolate()->clear_pending_exception();
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
}
TEST_F(UnoptimizedCompileJobTest, CompileAndRun) {
TEST_F(BackgroundCompileTaskTest, CompileAndRun) {
const char raw_script[] =
"function g() {\n"
" f = function(a) {\n"
......@@ -156,21 +124,19 @@ TEST_F(UnoptimizedCompileJobTest, CompileAndRun) {
Handle<JSFunction> f = RunJS<JSFunction>(script);
Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
ASSERT_FALSE(shared->is_compiled());
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared));
job->Compile(false);
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
task->Run();
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
ASSERT_TRUE(shared->is_compiled());
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
Smi* value = Smi::cast(*RunJS("f(100);"));
ASSERT_TRUE(value == Smi::FromInt(160));
}
TEST_F(UnoptimizedCompileJobTest, CompileFailure) {
TEST_F(BackgroundCompileTaskTest, CompileFailure) {
std::string raw_script("() { var a = ");
for (int i = 0; i < 10000; i++) {
// TODO(leszeks): Figure out a more "unit-test-y" way of forcing an analysis
......@@ -184,42 +150,35 @@ TEST_F(UnoptimizedCompileJobTest, CompileFailure) {
new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(isolate(), script);
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared, 100));
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared, 100));
job->Compile(false);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kReadyToFinalize, job);
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_TRUE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kFailed, job);
task->Run();
ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
ASSERT_TRUE(isolate()->has_pending_exception());
isolate()->clear_pending_exception();
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
}
class CompileTask : public Task {
public:
CompileTask(UnoptimizedCompileJob* job, base::Semaphore* semaphore)
: job_(job), semaphore_(semaphore) {}
CompileTask(BackgroundCompileTask* task, base::Semaphore* semaphore)
: task_(task), semaphore_(semaphore) {}
~CompileTask() override = default;
void Run() override {
job_->Compile(true);
ASSERT_FALSE(job_->IsFailed());
task_->Run();
semaphore_->Signal();
}
private:
UnoptimizedCompileJob* job_;
BackgroundCompileTask* task_;
base::Semaphore* semaphore_;
DISALLOW_COPY_AND_ASSIGN(CompileTask);
};
TEST_F(UnoptimizedCompileJobTest, CompileOnBackgroundThread) {
TEST_F(BackgroundCompileTaskTest, CompileOnBackgroundThread) {
const char* raw_script =
"(a, b) {\n"
" var c = a + b;\n"
......@@ -231,24 +190,20 @@ TEST_F(UnoptimizedCompileJobTest, CompileOnBackgroundThread) {
new test::ScriptResource(raw_script, strlen(raw_script));
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(isolate(), script);
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared));
base::Semaphore semaphore(0);
auto background_task = base::make_unique<CompileTask>(job.get(), &semaphore);
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
auto background_task = base::make_unique<CompileTask>(task.get(), &semaphore);
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(background_task));
semaphore.Wait();
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
ASSERT_TRUE(shared->is_compiled());
}
TEST_F(UnoptimizedCompileJobTest, EagerInnerFunctions) {
TEST_F(BackgroundCompileTaskTest, EagerInnerFunctions) {
const char raw_script[] =
"function g() {\n"
" f = function() {\n"
......@@ -264,25 +219,20 @@ TEST_F(UnoptimizedCompileJobTest, EagerInnerFunctions) {
Handle<JSFunction> f = RunJS<JSFunction>(script);
Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
ASSERT_FALSE(shared->is_compiled());
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
job->Compile(false);
ASSERT_FALSE(job->IsFailed());
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared));
task->Run();
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
ASSERT_TRUE(shared->is_compiled());
Handle<JSFunction> e = RunJS<JSFunction>("f();");
ASSERT_TRUE(e->shared()->is_compiled());
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
}
TEST_F(UnoptimizedCompileJobTest, LazyInnerFunctions) {
TEST_F(BackgroundCompileTaskTest, LazyInnerFunctions) {
const char raw_script[] =
"function g() {\n"
" f = function() {\n"
......@@ -297,25 +247,18 @@ TEST_F(UnoptimizedCompileJobTest, LazyInnerFunctions) {
Handle<JSFunction> f = RunJS<JSFunction>(script);
Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
ASSERT_FALSE(shared->is_compiled());
std::unique_ptr<UnoptimizedCompileJob> job(
NewUnoptimizedCompileJob(isolate(), shared));
job->Compile(false);
ASSERT_FALSE(job->IsFailed());
job->FinalizeOnMainThread(isolate(), shared);
ASSERT_FALSE(job->IsFailed());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
std::unique_ptr<BackgroundCompileTask> task(
NewBackgroundCompileTask(isolate(), shared));
task->Run();
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
ASSERT_TRUE(shared->is_compiled());
Handle<JSFunction> e = RunJS<JSFunction>("f();");
ASSERT_FALSE(e->shared()->is_compiled());
job->ResetOnMainThread(isolate());
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
}
#undef ASSERT_JOB_STATUS
} // namespace internal
} // namespace v8
// Copyright 2016 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/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
TEST(CompilerDispatcherTracerTest, EstimateWithoutSamples) {
CompilerDispatcherTracer tracer(nullptr);
EXPECT_EQ(0.0, tracer.EstimatePrepareInMs());
EXPECT_EQ(1.0, tracer.EstimateCompileInMs(1));
EXPECT_EQ(1.0, tracer.EstimateCompileInMs(42));
EXPECT_EQ(0.0, tracer.EstimateFinalizeInMs());
}
TEST(CompilerDispatcherTracerTest, Average) {
CompilerDispatcherTracer tracer(nullptr);
EXPECT_EQ(0.0, tracer.EstimatePrepareInMs());
tracer.RecordPrepare(1.0);
tracer.RecordPrepare(2.0);
tracer.RecordPrepare(3.0);
EXPECT_EQ((1.0 + 2.0 + 3.0) / 3, tracer.EstimatePrepareInMs());
}
TEST(CompilerDispatcherTracerTest, SizeBasedAverage) {
CompilerDispatcherTracer tracer(nullptr);
EXPECT_EQ(1.0, tracer.EstimateCompileInMs(100));
// All three samples parse 100 units/ms.
tracer.RecordCompile(1.0, 100);
tracer.RecordCompile(2.0, 200);
tracer.RecordCompile(3.0, 300);
EXPECT_EQ(1.0, tracer.EstimateCompileInMs(100));
EXPECT_EQ(5.0, tracer.EstimateCompileInMs(500));
}
} // namespace internal
} // namespace v8
......@@ -13,9 +13,6 @@
#include "src/ast/scopes.h"
#include "src/base/platform/semaphore.h"
#include "src/base/template-utils.h"
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/compiler-dispatcher/unoptimized-compile-job.h"
#include "src/compiler.h"
#include "src/flags.h"
#include "src/handles.h"
......@@ -355,9 +352,10 @@ TEST_F(CompilerDispatcherTest, IsEnqueued) {
dispatcher.AbortAll(BlockingBehavior::kBlock);
ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.WorkerTasksPending());
platform.ClearWorkerTasks();
platform.ClearIdleTask();
}
TEST_F(CompilerDispatcherTest, FinishNow) {
......@@ -377,12 +375,12 @@ TEST_F(CompilerDispatcherTest, FinishNow) {
ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
platform.ClearWorkerTasks();
platform.ClearIdleTask();
ASSERT_FALSE(platform.IdleTaskPending());
}
TEST_F(CompilerDispatcherTest, IdleTask) {
TEST_F(CompilerDispatcherTest, CompileAndFinalize) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
......@@ -393,11 +391,10 @@ TEST_F(CompilerDispatcherTest, IdleTask) {
base::Optional<CompilerDispatcher::JobId> job_id =
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_TRUE(platform.WorkerTasksPending());
// Since time doesn't progress on the MockPlatform, this is enough idle time
// to finish compiling the function.
platform.RunIdleTask(1000.0, 0.0);
// Run compile steps.
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
// Since we haven't yet registered the SFI for the job, it should still be
// enqueued and waiting.
......@@ -405,7 +402,7 @@ TEST_F(CompilerDispatcherTest, IdleTask) {
ASSERT_FALSE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
// Register SFI, which should schedule another idle task to complete the
// Register SFI, which should schedule another idle task to finalize the
// compilation.
dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
ASSERT_TRUE(platform.IdleTaskPending());
......@@ -413,10 +410,11 @@ TEST_F(CompilerDispatcherTest, IdleTask) {
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled());
platform.ClearWorkerTasks();
ASSERT_FALSE(platform.WorkerTasksPending());
ASSERT_FALSE(platform.IdleTaskPending());
}
TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
TEST_F(CompilerDispatcherTest, IdleTaskNoIdleTime) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
......@@ -429,25 +427,24 @@ TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
ASSERT_TRUE(platform.IdleTaskPending());
// Run compile steps.
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
// The job should be scheduled for the main thread.
// Job should be ready to finalize.
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(platform.IdleTaskPending());
// Only grant a little idle time and have time advance beyond it in one step.
platform.RunIdleTask(2.0, 1.0);
// Grant no idle time and have time advance beyond it in one step.
platform.RunIdleTask(0.0, 1.0);
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
// The job should be still scheduled for the main thread, but ready for
// finalization.
// Job should be ready to finalize.
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kReadyToFinalize,
dispatcher.jobs_.begin()->second->status());
ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
// Now grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0);
......@@ -455,7 +452,56 @@ TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
platform.ClearWorkerTasks();
ASSERT_FALSE(platform.WorkerTasksPending());
}
TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
Handle<SharedFunctionInfo> shared_1 =
test::CreateSharedFunctionInfo(i_isolate(), nullptr);
ASSERT_FALSE(shared_1->is_compiled());
Handle<SharedFunctionInfo> shared_2 =
test::CreateSharedFunctionInfo(i_isolate(), nullptr);
ASSERT_FALSE(shared_2->is_compiled());
base::Optional<CompilerDispatcher::JobId> job_id_1 =
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
base::Optional<CompilerDispatcher::JobId> job_id_2 =
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
// Run compile steps.
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
// Both jobs should be ready to finalize.
ASSERT_EQ(dispatcher.jobs_.size(), 2u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE((++dispatcher.jobs_.begin())->second->has_run);
ASSERT_TRUE(platform.IdleTaskPending());
// Grant a small anount of idle time and have time advance beyond it in one
// step.
platform.RunIdleTask(2.0, 1.0);
// Only one of the jobs should be finalized.
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_NE(dispatcher.IsEnqueued(shared_1), dispatcher.IsEnqueued(shared_2));
ASSERT_NE(shared_1->is_compiled(), shared_2->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
// Now grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared_1) ||
dispatcher.IsEnqueued(shared_2));
ASSERT_TRUE(shared_1->is_compiled() && shared_2->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
}
TEST_F(CompilerDispatcherTest, IdleTaskException) {
......@@ -478,8 +524,8 @@ TEST_F(CompilerDispatcherTest, IdleTaskException) {
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
// Since time doesn't progress on the MockPlatform, this is enough idle time
// to finish compiling the function.
// Run compile steps and finalize.
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
......@@ -488,41 +534,6 @@ TEST_F(CompilerDispatcherTest, IdleTaskException) {
platform.ClearWorkerTasks();
}
TEST_F(CompilerDispatcherTest, CompileOnBackgroundThread) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
Handle<SharedFunctionInfo> shared =
test::CreateSharedFunctionInfo(i_isolate(), nullptr);
ASSERT_FALSE(shared->is_compiled());
base::Optional<CompilerDispatcher::JobId> job_id =
EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_TRUE(platform.WorkerTasksPending());
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
ASSERT_EQ(UnoptimizedCompileJob::Status::kReadyToFinalize,
dispatcher.jobs_.begin()->second->status());
// Now grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(shared->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
}
TEST_F(CompilerDispatcherTest, FinishNowWithWorkerTask) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
......@@ -536,14 +547,12 @@ TEST_F(CompilerDispatcherTest, FinishNowWithWorkerTask) {
dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(platform.WorkerTasksPending());
// This does not block, but races with the FinishNow() call below.
......@@ -579,15 +588,16 @@ TEST_F(CompilerDispatcherTest, IdleTaskMultipleJobs) {
ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
ASSERT_TRUE(dispatcher.IsEnqueued(shared_2));
// Since time doesn't progress on the MockPlatform, this is enough idle time
// to finish compiling the function.
// Run compile steps and finalize.
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared_1));
ASSERT_FALSE(dispatcher.IsEnqueued(shared_2));
ASSERT_TRUE(shared_1->is_compiled());
ASSERT_TRUE(shared_2->is_compiled());
platform.ClearWorkerTasks();
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
}
TEST_F(CompilerDispatcherTest, FinishNowException) {
......@@ -617,7 +627,7 @@ TEST_F(CompilerDispatcherTest, FinishNowException) {
ASSERT_TRUE(i_isolate()->has_pending_exception());
i_isolate()->clear_pending_exception();
platform.ClearIdleTask();
ASSERT_FALSE(platform.IdleTaskPending());
platform.ClearWorkerTasks();
}
......@@ -634,8 +644,7 @@ TEST_F(CompilerDispatcherTest, AsyncAbortAllPendingWorkerTask) {
dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_TRUE(platform.WorkerTasksPending());
......@@ -670,11 +679,10 @@ TEST_F(CompilerDispatcherTest, AsyncAbortAllRunningWorkerTask) {
dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
ASSERT_FALSE(shared_1->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.WorkerTasksPending());
// Kick off background tasks and freeze them.
......@@ -714,21 +722,20 @@ TEST_F(CompilerDispatcherTest, AsyncAbortAllRunningWorkerTask) {
ASSERT_FALSE(dispatcher.abort_);
}
ASSERT_TRUE(platform.IdleTaskPending());
platform.RunIdleTask(5.0, 1.0);
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
ASSERT_FALSE(platform.ForegroundTasksPending());
// Now it's possible to enqueue new functions again.
job_id_2 = EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
ASSERT_TRUE(job_id_2);
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.WorkerTasksPending());
ASSERT_FALSE(platform.ForegroundTasksPending());
dispatcher.AbortAll(BlockingBehavior::kBlock);
platform.ClearWorkerTasks();
platform.ClearIdleTask();
ASSERT_FALSE(platform.IdleTaskPending());
}
TEST_F(CompilerDispatcherTest, FinishNowDuringAbortAll) {
......@@ -746,9 +753,8 @@ TEST_F(CompilerDispatcherTest, FinishNowDuringAbortAll) {
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
ASSERT_FALSE(shared->is_compiled());
ASSERT_EQ(dispatcher.jobs_.size(), 1u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.WorkerTasksPending());
// Kick off background tasks and freeze them.
......@@ -769,11 +775,6 @@ TEST_F(CompilerDispatcherTest, FinishNowDuringAbortAll) {
ASSERT_TRUE(dispatcher.abort_);
}
// Run the idle task, which should have already been canceled and won't do
// anything.
ASSERT_TRUE(platform.IdleTaskPending());
platform.RunIdleTask(5.0, 1.0);
// While the background thread holds on to a job, it is still enqueued.
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
......@@ -827,7 +828,7 @@ TEST_F(CompilerDispatcherTest, MemoryPressure) {
dispatcher.MemoryPressureNotification(v8::MemoryPressureLevel::kCritical,
true);
ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
platform.ClearIdleTask();
ASSERT_FALSE(platform.IdleTaskPending());
platform.ClearWorkerTasks();
}
......@@ -887,7 +888,7 @@ TEST_F(CompilerDispatcherTest, MemoryPressureFromBackground) {
platform.RunForegroundTasks();
ASSERT_FALSE(platform.ForegroundTasksPending());
platform.ClearIdleTask();
ASSERT_FALSE(platform.IdleTaskPending());
platform.ClearWorkerTasks();
}
......@@ -972,16 +973,14 @@ TEST_F(CompilerDispatcherTest, CompileMultipleOnBackgroundThread) {
dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
ASSERT_EQ(dispatcher.jobs_.size(), 2u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
dispatcher.jobs_.begin()->second->status());
ASSERT_EQ(UnoptimizedCompileJob::Status::kInitial,
(++dispatcher.jobs_.begin())->second->status());
ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_FALSE((++dispatcher.jobs_.begin())->second->has_run);
ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
ASSERT_TRUE(dispatcher.IsEnqueued(shared_2));
ASSERT_FALSE(shared_1->is_compiled());
ASSERT_FALSE(shared_2->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.WorkerTasksPending());
platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
......@@ -989,10 +988,8 @@ TEST_F(CompilerDispatcherTest, CompileMultipleOnBackgroundThread) {
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.WorkerTasksPending());
ASSERT_EQ(dispatcher.jobs_.size(), 2u);
ASSERT_EQ(UnoptimizedCompileJob::Status::kReadyToFinalize,
dispatcher.jobs_.begin()->second->status());
ASSERT_EQ(UnoptimizedCompileJob::Status::kReadyToFinalize,
(++dispatcher.jobs_.begin())->second->status());
ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
ASSERT_TRUE((++dispatcher.jobs_.begin())->second->has_run);
// Now grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0);
......
......@@ -8,7 +8,6 @@
#include <memory>
#include "include/v8.h"
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/parsing/parse-info.h"
namespace v8 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment