Index: src/compiler-dispatcher/compiler-dispatcher.cc |
diff --git a/src/compiler-dispatcher/compiler-dispatcher.cc b/src/compiler-dispatcher/compiler-dispatcher.cc |
index 5cf10aa3eb8ac012143c4f4b486d0e69c3bcfc1c..bf7005a26ffe394466b5c0ed5cace315c36ea5d4 100644 |
--- a/src/compiler-dispatcher/compiler-dispatcher.cc |
+++ b/src/compiler-dispatcher/compiler-dispatcher.cc |
@@ -66,12 +66,59 @@ bool IsFinished(CompilerDispatcherJob* job) { |
job->status() == CompileJobStatus::kFailed; |
} |
+bool CanRunOnAnyThread(CompilerDispatcherJob* job) { |
+ return (job->status() == CompileJobStatus::kReadyToParse && |
+ job->can_parse_on_background_thread()) || |
+ (job->status() == CompileJobStatus::kReadyToCompile && |
+ job->can_compile_on_background_thread()); |
+} |
+ |
+void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) { |
+ DCHECK(CanRunOnAnyThread(job)); |
+ switch (job->status()) { |
+ case CompileJobStatus::kReadyToParse: |
+ job->Parse(); |
+ break; |
+ |
+ case CompileJobStatus::kReadyToCompile: |
+ job->Compile(); |
+ break; |
+ |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
// 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 |
+class CompilerDispatcher::BackgroundTask : public CancelableTask { |
+ public: |
+ BackgroundTask(Isolate* isolate, CompilerDispatcher* dispatcher); |
+ ~BackgroundTask() override; |
+ |
+ // CancelableTask implementation. |
+ void RunInternal() override; |
+ |
+ private: |
+ CompilerDispatcher* dispatcher_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BackgroundTask); |
+}; |
+ |
+CompilerDispatcher::BackgroundTask::BackgroundTask( |
+ Isolate* isolate, CompilerDispatcher* dispatcher) |
+ : CancelableTask(isolate), dispatcher_(dispatcher) {} |
+ |
+CompilerDispatcher::BackgroundTask::~BackgroundTask() {} |
+ |
+void CompilerDispatcher::BackgroundTask::RunInternal() { |
+ dispatcher_->DoBackgroundWork(); |
+} |
+ |
class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
public: |
IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); |
@@ -102,7 +149,10 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
platform_(platform), |
max_stack_size_(max_stack_size), |
tracer_(new CompilerDispatcherTracer(isolate_)), |
- idle_task_scheduled_(false) {} |
+ idle_task_scheduled_(false), |
+ num_scheduled_background_tasks_(0), |
+ num_available_background_jobs_(0), |
+ main_thread_blocking_on_job_(nullptr) {} |
CompilerDispatcher::~CompilerDispatcher() { |
// To avoid crashing in unit tests due to unfished jobs. |
@@ -131,19 +181,33 @@ bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
bool CompilerDispatcher::IsEnabled() const { |
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
- return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); |
+ return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate) && |
+ platform_->NumberOfAvailableBackgroundThreads() > 0; |
vogelheim
2017/01/02 17:16:24
Generally speaking, it would be nice if the dispat
jochen (gone - plz use gerrit)
2017/01/02 19:39:39
fair point. Since we only schedule background task
|
} |
bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
return GetJobFor(function) != jobs_.end(); |
} |
+void CompilerDispatcher::EnsureNotOnBackground(CompilerDispatcherJob* job) { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ if (running_background_jobs_.find(job) == running_background_jobs_.end()) { |
+ pending_background_jobs_.erase(job); |
vogelheim
2017/01/02 17:16:24
Do you need to update num_available_background_job
jochen (gone - plz use gerrit)
2017/01/02 19:39:39
actually, I don't need that variable anymore, so I
|
+ return; |
+ } |
+ DCHECK_NULL(main_thread_blocking_on_job_); |
+ main_thread_blocking_on_job_ = job; |
+ while (main_thread_blocking_on_job_ != nullptr) { |
+ main_thread_blocking_signal_.Wait(&mutex_); |
+ } |
+ DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end()); |
+} |
+ |
bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
JobMap::const_iterator job = GetJobFor(function); |
CHECK(job != jobs_.end()); |
- // TODO(jochen): Check if there's an in-flight background task working on this |
- // job. |
+ EnsureNotOnBackground(job->second.get()); |
while (!IsFinished(job->second.get())) { |
DoNextStepOnMainThread(isolate_, job->second.get(), |
ExceptionHandling::kThrow); |
@@ -160,17 +224,15 @@ void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, |
JobMap::const_iterator job = GetJobFor(function); |
CHECK(job != jobs_.end()); |
- // TODO(jochen): Check if there's an in-flight background task working on this |
- // job. |
+ EnsureNotOnBackground(job->second.get()); |
job->second->ResetOnMainThread(); |
jobs_.erase(job); |
} |
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_) { |
+ EnsureNotOnBackground(kv.second.get()); |
kv.second->ResetOnMainThread(); |
} |
jobs_.clear(); |
@@ -198,6 +260,56 @@ void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
new IdleTask(isolate_, this)); |
} |
+void CompilerDispatcher::ConsiderJobForBackgroundProcessing( |
+ CompilerDispatcherJob* job) { |
+ if (!CanRunOnAnyThread(job)) return; |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ pending_background_jobs_.insert(job); |
+ ++num_available_background_jobs_; |
+ } |
+ ScheduleMoreBackgroundTasksIfNeeded(); |
+} |
+ |
+void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ if (num_available_background_jobs_ == 0) return; |
+ if (platform_->NumberOfAvailableBackgroundThreads() <= |
+ num_scheduled_background_tasks_) { |
+ return; |
+ } |
+ ++num_scheduled_background_tasks_; |
+ platform_->CallOnBackgroundThread(new BackgroundTask(isolate_, this), |
+ v8::Platform::kShortRunningTask); |
+} |
+ |
+void CompilerDispatcher::DoBackgroundWork() { |
+ CompilerDispatcherJob* job = nullptr; |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ --num_scheduled_background_tasks_; |
+ if (!pending_background_jobs_.empty()) { |
+ auto it = pending_background_jobs_.begin(); |
+ job = *it; |
+ pending_background_jobs_.erase(it); |
+ running_background_jobs_.insert(job); |
+ --num_available_background_jobs_; |
+ } |
+ } |
+ if (job == nullptr) return; |
+ DoNextStepOnBackgroundThread(job); |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ running_background_jobs_.erase(job); |
+ |
+ if (main_thread_blocking_on_job_ == job) { |
+ main_thread_blocking_on_job_ = nullptr; |
+ main_thread_blocking_signal_.NotifyOne(); |
+ } |
+ } |
+ ScheduleMoreBackgroundTasksIfNeeded(); |
+} |
+ |
void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
idle_task_scheduled_ = false; |
@@ -214,6 +326,22 @@ void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
job != jobs_.end() && 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. |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ if (running_background_jobs_.find(job->second.get()) != |
+ running_background_jobs_.end()) { |
+ ++job; |
+ continue; |
+ } |
+ auto it = pending_background_jobs_.find(job->second.get()); |
+ if (it != pending_background_jobs_.end()) { |
+ pending_background_jobs_.erase(it); |
+ --num_available_background_jobs_; |
+ } |
vogelheim
2017/01/02 17:16:24
This logic is a bit weird, because first we take i
|
+ } |
vogelheim
2017/01/02 17:16:24
[opinion ahead; feel free to ignore:]
The can-id
jochen (gone - plz use gerrit)
2017/01/02 19:39:39
the can-idle property can change, when the estimat
vogelheim
2017/01/03 10:32:37
Hmm. Not sure I'm willing to give this up yet. For
jochen (gone - plz use gerrit)
2017/01/03 12:59:35
in d8, we do get predictable idle work calls, howe
|
double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); |
if (idle_time_in_seconds < |
(estimate_in_ms / |
@@ -222,6 +350,7 @@ void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
// 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; |
+ ConsiderJobForBackgroundProcessing(job->second.get()); |
++job; |
} else if (IsFinished(job->second.get())) { |
job->second->ResetOnMainThread(); |