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..47c04b8b44a39792173dd26443179bf137f9745e 100644 |
--- a/src/compiler-dispatcher/compiler-dispatcher.cc |
+++ b/src/compiler-dispatcher/compiler-dispatcher.cc |
@@ -66,15 +66,65 @@ 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, CancelableTaskManager* task_manager, |
+ CompilerDispatcher* dispatcher); |
+ ~BackgroundTask() override; |
+ |
+ // CancelableTask implementation. |
+ void RunInternal() override; |
+ |
+ private: |
+ CompilerDispatcher* dispatcher_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BackgroundTask); |
+}; |
+ |
+CompilerDispatcher::BackgroundTask::BackgroundTask( |
+ Isolate* isolate, CancelableTaskManager* task_manager, |
+ CompilerDispatcher* dispatcher) |
+ : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {} |
+ |
+CompilerDispatcher::BackgroundTask::~BackgroundTask() {} |
+ |
+void CompilerDispatcher::BackgroundTask::RunInternal() { |
+ dispatcher_->DoBackgroundWork(); |
+} |
+ |
class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
public: |
- IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); |
+ IdleTask(Isolate* isolate, CancelableTaskManager* task_manager, |
+ CompilerDispatcher* dispatcher); |
~IdleTask() override; |
// CancelableIdleTask implementation. |
@@ -87,8 +137,9 @@ class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
}; |
CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, |
+ CancelableTaskManager* task_manager, |
CompilerDispatcher* dispatcher) |
- : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} |
+ : CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {} |
CompilerDispatcher::IdleTask::~IdleTask() {} |
@@ -102,11 +153,15 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
platform_(platform), |
max_stack_size_(max_stack_size), |
tracer_(new CompilerDispatcherTracer(isolate_)), |
- idle_task_scheduled_(false) {} |
+ task_manager_(new CancelableTaskManager()), |
+ idle_task_scheduled_(false), |
+ num_scheduled_background_tasks_(0), |
+ main_thread_blocking_on_job_(nullptr) {} |
CompilerDispatcher::~CompilerDispatcher() { |
// To avoid crashing in unit tests due to unfished jobs. |
AbortAll(BlockingBehavior::kBlock); |
+ task_manager_->CancelAndWait(); |
} |
bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
@@ -138,12 +193,27 @@ bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
return GetJobFor(function) != jobs_.end(); |
} |
+void CompilerDispatcher::WaitForJobIfRunningOnBackground( |
+ CompilerDispatcherJob* job) { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ if (running_background_jobs_.find(job) == running_background_jobs_.end()) { |
+ pending_background_jobs_.erase(job); |
+ 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()); |
+ DCHECK(running_background_jobs_.find(job) == running_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. |
+ WaitForJobIfRunningOnBackground(job->second.get()); |
while (!IsFinished(job->second.get())) { |
DoNextStepOnMainThread(isolate_, job->second.get(), |
ExceptionHandling::kThrow); |
@@ -154,23 +224,11 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
return result; |
} |
-void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, |
- BlockingBehavior blocking) { |
- USE(blocking); |
- 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. |
- 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. |
+ // TODO(jochen): Implement support for non-blocking abort. |
+ DCHECK(blocking == BlockingBehavior::kBlock); |
for (auto& kv : jobs_) { |
+ WaitForJobIfRunningOnBackground(kv.second.get()); |
kv.second->ResetOnMainThread(); |
} |
jobs_.clear(); |
@@ -188,18 +246,87 @@ CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
return jobs_.end(); |
} |
-void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
+void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() { |
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
DCHECK(platform_->IdleTasksEnabled(v8_isolate)); |
- if (idle_task_scheduled_) return; |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ if (idle_task_scheduled_) return; |
+ idle_task_scheduled_ = true; |
+ } |
+ platform_->CallIdleOnForegroundThread( |
+ v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); |
+} |
+ |
+void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
if (jobs_.empty()) return; |
- idle_task_scheduled_ = true; |
- platform_->CallIdleOnForegroundThread(v8_isolate, |
- new IdleTask(isolate_, this)); |
+ ScheduleIdleTaskFromAnyThread(); |
+} |
+ |
+void CompilerDispatcher::ConsiderJobForBackgroundProcessing( |
+ CompilerDispatcherJob* job) { |
+ if (!CanRunOnAnyThread(job)) return; |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ pending_background_jobs_.insert(job); |
+ } |
+ ScheduleMoreBackgroundTasksIfNeeded(); |
+} |
+ |
+void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { |
+ if (FLAG_single_threaded) return; |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ if (pending_background_jobs_.empty()) return; |
+ if (platform_->NumberOfAvailableBackgroundThreads() <= |
+ num_scheduled_background_tasks_) { |
+ return; |
+ } |
+ ++num_scheduled_background_tasks_; |
+ } |
+ platform_->CallOnBackgroundThread( |
+ new BackgroundTask(isolate_, task_manager_.get(), 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); |
+ } |
+ } |
+ if (job == nullptr) return; |
+ DoNextStepOnBackgroundThread(job); |
+ |
+ ScheduleMoreBackgroundTasksIfNeeded(); |
+ // Unconditionally schedule an idle task, as all background steps have to be |
+ // followed by a main thread step. |
+ ScheduleIdleTaskFromAnyThread(); |
+ |
+ { |
+ 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(); |
+ } |
+ } |
+ // Don't touch |this| anymore after this point, as it might have been |
+ // deleted. |
} |
void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
- idle_task_scheduled_ = false; |
+ { |
+ base::LockGuard<base::Mutex> lock(&mutex_); |
+ idle_task_scheduled_ = false; |
+ } |
// Number of jobs that are unlikely to make progress during any idle callback |
// due to their estimated duration. |
@@ -214,6 +341,17 @@ 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. |
+ 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; |
+ } |
+ auto it = pending_background_jobs_.find(job->second.get()); |
double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); |
if (idle_time_in_seconds < |
(estimate_in_ms / |
@@ -222,14 +360,23 @@ 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; |
+ if (it == pending_background_jobs_.end()) { |
+ lock.reset(); |
+ ConsiderJobForBackgroundProcessing(job->second.get()); |
+ } |
++job; |
} else if (IsFinished(job->second.get())) { |
+ DCHECK(it == pending_background_jobs_.end()); |
job->second->ResetOnMainThread(); |
job = jobs_.erase(job); |
- break; |
+ continue; |
} else { |
// Do one step, and keep processing the job (as we don't advance the |
// iterator). |
+ if (it != pending_background_jobs_.end()) { |
+ pending_background_jobs_.erase(it); |
+ } |
+ lock.reset(); |
DoNextStepOnMainThread(isolate_, job->second.get(), |
ExceptionHandling::kSwallow); |
} |