| Index: src/compiler-dispatcher/compiler-dispatcher.cc
|
| diff --git a/src/compiler-dispatcher/compiler-dispatcher.cc b/src/compiler-dispatcher/compiler-dispatcher.cc
|
| index 8d503780cd5511b7af36cbbf865e6037277a5282..1b639fe9b0ad7035969b993a2253397461fa595c 100644
|
| --- a/src/compiler-dispatcher/compiler-dispatcher.cc
|
| +++ b/src/compiler-dispatcher/compiler-dispatcher.cc
|
| @@ -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)
|
| +class CompilerDispatcher::IdleTask : public CancelableIdleTask {
|
| + public:
|
| + IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher);
|
| + ~IdleTask() override;
|
| +
|
| + // 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_)) {}
|
| + tracer_(new CompilerDispatcherTracer(isolate_)),
|
| + idle_task_scheduled_(false) {}
|
|
|
| -CompilerDispatcher::~CompilerDispatcher() {}
|
| +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
|
|
|