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..64c3723bcf2f9ea6e91de9df40eed457a191394e 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, kKeep }; |
+ |
+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,10 @@ bool DoNextStepOnMainThread(CompilerDispatcherJob* job) { |
break; |
} |
+ if (exception_handling == ExceptionHandling::kKeep && try_catch.HasCaught()) { |
+ DCHECK(job->status() == CompileJobStatus::kFailed); |
vogelheim
2016/12/15 15:24:12
nitpick: DCHECK_EQ
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
that would require implementation operator<< for t
|
+ try_catch.ReThrow(); |
+ } |
return job->status() != CompileJobStatus::kFailed; |
} |
@@ -52,14 +65,48 @@ bool IsFinished(CompilerDispatcherJob* job) { |
job->status() == CompileJobStatus::kFailed; |
} |
+// Theoretically, we get 50ms of idle time max, however, it's unlikely that |
vogelheim
2016/12/15 15:24:11
super nitpick: Remove some of the commas, like:
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
done
|
+// 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 |
@@ -74,7 +121,9 @@ bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
isolate_, tracer_.get(), function, max_stack_size_)); |
std::pair<int, int> key(Script::cast(function->script())->id(), |
function->function_literal_id()); |
- jobs_.insert(std::make_pair(key, std::move(job))); |
+ JobMap::const_iterator it = jobs_.insert(std::make_pair(key, std::move(job))); |
+ ClassifyJob(it); |
+ ScheduleIdleTaskIfNeeded(); |
return true; |
} |
@@ -89,9 +138,13 @@ 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::kKeep); |
} |
bool result = job->second->status() != CompileJobStatus::kFailed; |
+ job->second->ResetOnMainThread(); |
+ jobs_for_background_thread_.erase(job); |
+ jobs_for_main_thread_.erase(job); |
jobs_.erase(job); |
return result; |
} |
@@ -104,6 +157,9 @@ 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_for_background_thread_.erase(job); |
+ jobs_for_main_thread_.erase(job); |
jobs_.erase(job); |
} |
@@ -111,6 +167,11 @@ 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_for_background_thread_.clear(); |
+ jobs_for_main_thread_.clear(); |
jobs_.clear(); |
} |
@@ -126,5 +187,89 @@ 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; |
+ |
+ DoIdleWorkForJobSet(deadline_in_seconds, &jobs_for_main_thread_); |
+ if (deadline_in_seconds <= platform_->MonotonicallyIncreasingTime()) return; |
+ DoIdleWorkForJobSet(deadline_in_seconds, &jobs_for_background_thread_); |
+} |
+ |
+void CompilerDispatcher::DoIdleWorkForJobSet( |
marja
2016/12/15 15:56:43
Can you add some high-level comments to CompilerDi
|
+ double deadline_in_seconds, |
+ std::unordered_set<JobMap::const_iterator, JobIteratorHash>* job_set) { |
+ bool work_left = false; |
+ |
+ for (auto it = job_set->begin(); it != job_set->end();) { |
+ double idle_time_in_seconds = |
+ deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
+ if (idle_time_in_seconds <= 0.0) { |
+ work_left = true; |
+ break; |
+ } |
+ |
+ auto job = (*it); |
+ // We'll put the job back on the right queue if we don't manage to process |
+ // it completely. |
+ it = jobs_for_main_thread_.erase(it); |
marja
2016/12/15 15:56:43
How is this supposed to work? it is an iterator fo
jochen (gone - plz use gerrit)
2016/12/15 20:18:44
erase() in C++11 returns the next iterator, it's s
|
+ |
+ // Advance the current job as much as possible. |
+ for (;;) { |
+ 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) work_left = true; |
+ // As there's remaining work, put the job back on the queue. |
+ ClassifyJob(job); |
marja
2016/12/15 15:56:43
As job_set is a pointer to one of the underlying u
marja
2016/12/15 15:56:43
The interaction between the queues and the job set
jochen (gone - plz use gerrit)
2016/12/15 20:18:44
once we also have a background scheduler, they'll
|
+ break; |
+ } |
+ DoNextStepOnMainThread(isolate_, job->second.get(), |
+ ExceptionHandling::kSwallow); |
+ |
+ if (IsFinished(job->second.get())) { |
+ job->second->ResetOnMainThread(); |
+ jobs_.erase(job); |
marja
2016/12/15 15:56:43
There's jobs_ and job_set, could you name them som
|
+ break; |
+ } |
+ |
+ idle_time_in_seconds = |
+ deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
+ } |
+ } |
+ if (work_left) ScheduleIdleTaskIfNeeded(); |
vogelheim
2016/12/15 15:24:12
This would potentially be called twice, since DoId
vogelheim
2016/12/15 15:24:12
At this point, wouldn't (work_left == !job_set.emp
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
I found it more readable to put it here, instead o
|
+} |
+ |
+void CompilerDispatcher::ClassifyJob(JobMap::const_iterator it) { |
marja
2016/12/15 15:56:43
This function name is misleading, it doesn't just
|
+ bool can_go_on_background_thread = false; |
+ CompilerDispatcherJob* job = it->second.get(); |
+ if (job->status() == CompileJobStatus::kReadyToParse && |
+ job->can_parse_on_background_thread()) { |
+ can_go_on_background_thread = true; |
+ } else if (job->status() == CompileJobStatus::kReadyToCompile && |
+ job->can_compile_on_background_thread()) { |
+ can_go_on_background_thread = true; |
+ } |
vogelheim
2016/12/15 15:24:11
style nitpick; feel free to ignore... The if-else
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
done
|
+ |
+ if (can_go_on_background_thread) { |
+ jobs_for_background_thread_.insert(it); |
+ } else { |
+ jobs_for_main_thread_.insert(it); |
+ } |
+} |
+ |
} // namespace internal |
} // namespace v8 |