Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1083)

Unified Diff: src/compiler-dispatcher/compiler-dispatcher.cc

Issue 2606263002: Use background tasks for the compiler dispatcher (Closed)
Patch Set: unit tests Created 3 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..59622dc88ac25aa79301df616ddfff5be6229edc 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,9 @@ 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),
+ main_thread_blocking_on_job_(nullptr) {}
CompilerDispatcher::~CompilerDispatcher() {
// To avoid crashing in unit tests due to unfished jobs.
@@ -138,12 +187,25 @@ bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
return GetJobFor(function) != jobs_.end();
}
+void CompilerDispatcher::EnsureNotOnBackground(CompilerDispatcherJob* job) {
marja 2017/01/03 10:42:41 This function name is confusing. I had to read it
jochen (gone - plz use gerrit) 2017/01/03 12:59:35 done
+ 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());
vogelheim 2017/01/03 10:32:37 Also the same DCHECK(...), with running_background
jochen (gone - plz use gerrit) 2017/01/03 12:59:36 done
+}
+
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);
@@ -154,23 +216,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);
vogelheim 2017/01/03 10:32:37 DCHECK_EQ ?
jochen (gone - plz use gerrit) 2017/01/03 12:59:36 doesn't work with enum classes
for (auto& kv : jobs_) {
+ EnsureNotOnBackground(kv.second.get());
kv.second->ResetOnMainThread();
}
jobs_.clear();
@@ -188,18 +238,80 @@ 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;
- if (jobs_.empty()) return;
- idle_task_scheduled_ = true;
+ {
+ base::LockGuard<base::Mutex> lock(&mutex_);
+ if (idle_task_scheduled_) return;
+ idle_task_scheduled_ = true;
+ }
platform_->CallIdleOnForegroundThread(v8_isolate,
new IdleTask(isolate_, this));
}
+void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
+ if (jobs_.empty()) return;
+ 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;
vogelheim 2017/01/03 10:32:37 Hmm. So if --single_threaded, ConsiderJobForBackgr
jochen (gone - plz use gerrit) 2017/01/03 12:59:36 if the job is too big for idle time. Since the DoI
+ {
+ 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_, this),
+ v8::Platform::kShortRunningTask);
+}
+
+void CompilerDispatcher::DoBackgroundWork() {
+ CompilerDispatcherJob* job = nullptr;
marja 2017/01/03 10:43:41 Can you add an assert (somewhere, maybe here) that
jochen (gone - plz use gerrit) 2017/01/03 12:59:35 that assert will not necessarily hold: the embedde
marja 2017/01/03 13:14:57 Hmm, I'm missing something... ScheduleMoreBackgro
jochen (gone - plz use gerrit) 2017/01/03 14:01:44 assume that each task counts down num_scheduled_ba
marja 2017/01/03 14:22:45 Ah I see It's slightly unintuitive though that nu
+ {
+ 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);
+ {
+ 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();
+ ScheduleIdleTaskFromAnyThread();
vogelheim 2017/01/03 10:32:37 ScheduleIdleTaskFromAnyThread unconditionally sche
jochen (gone - plz use gerrit) 2017/01/03 12:59:36 correct. Added a comment
+}
+
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 +326,21 @@ 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());
marja 2017/01/03 10:42:41 This is kinda weird... here we erase the job from
jochen (gone - plz use gerrit) 2017/01/03 12:59:36 we're always working on it in the sense that we in
marja 2017/01/03 13:14:57 If the job is too big (or the amount of idle time
jochen (gone - plz use gerrit) 2017/01/03 14:01:44 what about this?
+ if (it != pending_background_jobs_.end()) {
+ pending_background_jobs_.erase(it);
+ }
+ }
double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
if (idle_time_in_seconds <
(estimate_in_ms /
@@ -222,6 +349,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());
marja 2017/01/03 10:42:40 This is somewhat surprising... so we only push tho
jochen (gone - plz use gerrit) 2017/01/03 12:59:36 the other cases are: - the job is finished (no ne
marja 2017/01/03 13:14:57 But if there are a zillion of small tasks and seve
jochen (gone - plz use gerrit) 2017/01/03 14:01:44 added a lengthy comment
++job;
} else if (IsFinished(job->second.get())) {
job->second->ResetOnMainThread();

Powered by Google App Engine
This is Rietveld 408576698