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 |