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

Side by Side Diff: src/compiler-dispatcher/compiler-dispatcher.cc

Issue 2606263002: Use background tasks for the compiler dispatcher (Closed)
Patch Set: updates Created 3 years, 11 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler-dispatcher/compiler-dispatcher.h" 5 #include "src/compiler-dispatcher/compiler-dispatcher.h"
6 6
7 #include "include/v8-platform.h" 7 #include "include/v8-platform.h"
8 #include "include/v8.h" 8 #include "include/v8.h"
9 #include "src/base/platform/time.h" 9 #include "src/base/platform/time.h"
10 #include "src/cancelable-task.h" 10 #include "src/cancelable-task.h"
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 try_catch.ReThrow(); 59 try_catch.ReThrow();
60 } 60 }
61 return job->status() != CompileJobStatus::kFailed; 61 return job->status() != CompileJobStatus::kFailed;
62 } 62 }
63 63
64 bool IsFinished(CompilerDispatcherJob* job) { 64 bool IsFinished(CompilerDispatcherJob* job) {
65 return job->status() == CompileJobStatus::kDone || 65 return job->status() == CompileJobStatus::kDone ||
66 job->status() == CompileJobStatus::kFailed; 66 job->status() == CompileJobStatus::kFailed;
67 } 67 }
68 68
69 bool CanRunOnAnyThread(CompilerDispatcherJob* job) {
70 return (job->status() == CompileJobStatus::kReadyToParse &&
71 job->can_parse_on_background_thread()) ||
72 (job->status() == CompileJobStatus::kReadyToCompile &&
73 job->can_compile_on_background_thread());
74 }
75
76 void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) {
77 DCHECK(CanRunOnAnyThread(job));
78 switch (job->status()) {
79 case CompileJobStatus::kReadyToParse:
80 job->Parse();
81 break;
82
83 case CompileJobStatus::kReadyToCompile:
84 job->Compile();
85 break;
86
87 default:
88 UNREACHABLE();
89 }
90 }
91
69 // Theoretically we get 50ms of idle time max, however it's unlikely that 92 // Theoretically we get 50ms of idle time max, however it's unlikely that
70 // we'll get all of it so try to be a conservative. 93 // we'll get all of it so try to be a conservative.
71 const double kMaxIdleTimeToExpectInMs = 40; 94 const double kMaxIdleTimeToExpectInMs = 40;
72 95
73 } // namespace 96 } // namespace
74 97
98 class CompilerDispatcher::BackgroundTask : public CancelableTask {
99 public:
100 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager,
101 CompilerDispatcher* dispatcher);
102 ~BackgroundTask() override;
103
104 // CancelableTask implementation.
105 void RunInternal() override;
106
107 private:
108 CompilerDispatcher* dispatcher_;
109
110 DISALLOW_COPY_AND_ASSIGN(BackgroundTask);
111 };
112
113 CompilerDispatcher::BackgroundTask::BackgroundTask(
114 Isolate* isolate, CancelableTaskManager* task_manager,
115 CompilerDispatcher* dispatcher)
116 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {}
117
118 CompilerDispatcher::BackgroundTask::~BackgroundTask() {}
119
120 void CompilerDispatcher::BackgroundTask::RunInternal() {
121 dispatcher_->DoBackgroundWork();
122 }
123
75 class CompilerDispatcher::IdleTask : public CancelableIdleTask { 124 class CompilerDispatcher::IdleTask : public CancelableIdleTask {
76 public: 125 public:
77 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); 126 IdleTask(Isolate* isolate, CancelableTaskManager* task_manager,
127 CompilerDispatcher* dispatcher);
78 ~IdleTask() override; 128 ~IdleTask() override;
79 129
80 // CancelableIdleTask implementation. 130 // CancelableIdleTask implementation.
81 void RunInternal(double deadline_in_seconds) override; 131 void RunInternal(double deadline_in_seconds) override;
82 132
83 private: 133 private:
84 CompilerDispatcher* dispatcher_; 134 CompilerDispatcher* dispatcher_;
85 135
86 DISALLOW_COPY_AND_ASSIGN(IdleTask); 136 DISALLOW_COPY_AND_ASSIGN(IdleTask);
87 }; 137 };
88 138
89 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, 139 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate,
140 CancelableTaskManager* task_manager,
90 CompilerDispatcher* dispatcher) 141 CompilerDispatcher* dispatcher)
91 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} 142 : CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {}
92 143
93 CompilerDispatcher::IdleTask::~IdleTask() {} 144 CompilerDispatcher::IdleTask::~IdleTask() {}
94 145
95 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { 146 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) {
96 dispatcher_->DoIdleWork(deadline_in_seconds); 147 dispatcher_->DoIdleWork(deadline_in_seconds);
97 } 148 }
98 149
99 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, 150 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
100 size_t max_stack_size) 151 size_t max_stack_size)
101 : isolate_(isolate), 152 : isolate_(isolate),
102 platform_(platform), 153 platform_(platform),
103 max_stack_size_(max_stack_size), 154 max_stack_size_(max_stack_size),
104 tracer_(new CompilerDispatcherTracer(isolate_)), 155 tracer_(new CompilerDispatcherTracer(isolate_)),
105 idle_task_scheduled_(false) {} 156 task_manager_(new CancelableTaskManager()),
157 idle_task_scheduled_(false),
158 num_scheduled_background_tasks_(0),
159 main_thread_blocking_on_job_(nullptr) {}
106 160
107 CompilerDispatcher::~CompilerDispatcher() { 161 CompilerDispatcher::~CompilerDispatcher() {
108 // To avoid crashing in unit tests due to unfished jobs. 162 // To avoid crashing in unit tests due to unfished jobs.
109 AbortAll(BlockingBehavior::kBlock); 163 AbortAll(BlockingBehavior::kBlock);
164 task_manager_->CancelAndWait();
110 } 165 }
111 166
112 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { 167 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
113 if (!IsEnabled()) return false; 168 if (!IsEnabled()) return false;
114 169
115 // We only handle functions (no eval / top-level code / wasm) that are 170 // We only handle functions (no eval / top-level code / wasm) that are
116 // attached to a script. 171 // attached to a script.
117 if (!function->script()->IsScript() || !function->is_function() || 172 if (!function->script()->IsScript() || !function->is_function() ||
118 function->asm_function() || function->native()) { 173 function->asm_function() || function->native()) {
119 return false; 174 return false;
(...skipping 11 matching lines...) Expand all
131 186
132 bool CompilerDispatcher::IsEnabled() const { 187 bool CompilerDispatcher::IsEnabled() const {
133 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); 188 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
134 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); 189 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate);
135 } 190 }
136 191
137 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { 192 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
138 return GetJobFor(function) != jobs_.end(); 193 return GetJobFor(function) != jobs_.end();
139 } 194 }
140 195
196 void CompilerDispatcher::WaitForJobIfRunningOnBackground(
197 CompilerDispatcherJob* job) {
198 base::LockGuard<base::Mutex> lock(&mutex_);
199 if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
200 pending_background_jobs_.erase(job);
201 return;
202 }
203 DCHECK_NULL(main_thread_blocking_on_job_);
204 main_thread_blocking_on_job_ = job;
205 while (main_thread_blocking_on_job_ != nullptr) {
206 main_thread_blocking_signal_.Wait(&mutex_);
207 }
208 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end());
209 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end());
210 }
211
141 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { 212 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
142 JobMap::const_iterator job = GetJobFor(function); 213 JobMap::const_iterator job = GetJobFor(function);
143 CHECK(job != jobs_.end()); 214 CHECK(job != jobs_.end());
144 215
145 // TODO(jochen): Check if there's an in-flight background task working on this 216 WaitForJobIfRunningOnBackground(job->second.get());
146 // job.
147 while (!IsFinished(job->second.get())) { 217 while (!IsFinished(job->second.get())) {
148 DoNextStepOnMainThread(isolate_, job->second.get(), 218 DoNextStepOnMainThread(isolate_, job->second.get(),
149 ExceptionHandling::kThrow); 219 ExceptionHandling::kThrow);
150 } 220 }
151 bool result = job->second->status() != CompileJobStatus::kFailed; 221 bool result = job->second->status() != CompileJobStatus::kFailed;
152 job->second->ResetOnMainThread(); 222 job->second->ResetOnMainThread();
153 jobs_.erase(job); 223 jobs_.erase(job);
154 return result; 224 return result;
155 } 225 }
156 226
157 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function,
158 BlockingBehavior blocking) {
159 USE(blocking);
160 JobMap::const_iterator job = GetJobFor(function);
161 CHECK(job != jobs_.end());
162
163 // TODO(jochen): Check if there's an in-flight background task working on this
164 // job.
165 job->second->ResetOnMainThread();
166 jobs_.erase(job);
167 }
168
169 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { 227 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
170 USE(blocking); 228 // TODO(jochen): Implement support for non-blocking abort.
171 // TODO(jochen): Check if there's an in-flight background task working on this 229 DCHECK(blocking == BlockingBehavior::kBlock);
172 // job.
173 for (auto& kv : jobs_) { 230 for (auto& kv : jobs_) {
231 WaitForJobIfRunningOnBackground(kv.second.get());
174 kv.second->ResetOnMainThread(); 232 kv.second->ResetOnMainThread();
175 } 233 }
176 jobs_.clear(); 234 jobs_.clear();
177 } 235 }
178 236
179 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( 237 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
180 Handle<SharedFunctionInfo> shared) const { 238 Handle<SharedFunctionInfo> shared) const {
181 if (!shared->script()->IsScript()) return jobs_.end(); 239 if (!shared->script()->IsScript()) return jobs_.end();
182 std::pair<int, int> key(Script::cast(shared->script())->id(), 240 std::pair<int, int> key(Script::cast(shared->script())->id(),
183 shared->function_literal_id()); 241 shared->function_literal_id());
184 auto range = jobs_.equal_range(key); 242 auto range = jobs_.equal_range(key);
185 for (auto job = range.first; job != range.second; ++job) { 243 for (auto job = range.first; job != range.second; ++job) {
186 if (job->second->IsAssociatedWith(shared)) return job; 244 if (job->second->IsAssociatedWith(shared)) return job;
187 } 245 }
188 return jobs_.end(); 246 return jobs_.end();
189 } 247 }
190 248
191 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { 249 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() {
192 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); 250 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
193 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); 251 DCHECK(platform_->IdleTasksEnabled(v8_isolate));
194 if (idle_task_scheduled_) return; 252 {
253 base::LockGuard<base::Mutex> lock(&mutex_);
254 if (idle_task_scheduled_) return;
255 idle_task_scheduled_ = true;
256 }
257 platform_->CallIdleOnForegroundThread(
258 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this));
259 }
260
261 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
195 if (jobs_.empty()) return; 262 if (jobs_.empty()) return;
196 idle_task_scheduled_ = true; 263 ScheduleIdleTaskFromAnyThread();
197 platform_->CallIdleOnForegroundThread(v8_isolate, 264 }
198 new IdleTask(isolate_, this)); 265
266 void CompilerDispatcher::ConsiderJobForBackgroundProcessing(
267 CompilerDispatcherJob* job) {
268 if (!CanRunOnAnyThread(job)) return;
269 {
270 base::LockGuard<base::Mutex> lock(&mutex_);
271 pending_background_jobs_.insert(job);
272 }
273 ScheduleMoreBackgroundTasksIfNeeded();
274 }
275
276 void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() {
277 if (FLAG_single_threaded) return;
278 {
279 base::LockGuard<base::Mutex> lock(&mutex_);
280 if (pending_background_jobs_.empty()) return;
281 if (platform_->NumberOfAvailableBackgroundThreads() <=
282 num_scheduled_background_tasks_) {
283 return;
284 }
285 ++num_scheduled_background_tasks_;
286 }
287 platform_->CallOnBackgroundThread(
288 new BackgroundTask(isolate_, task_manager_.get(), this),
289 v8::Platform::kShortRunningTask);
290 }
291
292 void CompilerDispatcher::DoBackgroundWork() {
293 CompilerDispatcherJob* job = nullptr;
294 {
295 base::LockGuard<base::Mutex> lock(&mutex_);
296 --num_scheduled_background_tasks_;
297 if (!pending_background_jobs_.empty()) {
298 auto it = pending_background_jobs_.begin();
299 job = *it;
300 pending_background_jobs_.erase(it);
301 running_background_jobs_.insert(job);
302 }
303 }
304 if (job == nullptr) return;
305 DoNextStepOnBackgroundThread(job);
306
307 ScheduleMoreBackgroundTasksIfNeeded();
308 // Unconditionally schedule an idle task, as all background steps have to be
309 // followed by a main thread step.
310 ScheduleIdleTaskFromAnyThread();
311
312 {
313 base::LockGuard<base::Mutex> lock(&mutex_);
314 running_background_jobs_.erase(job);
315
316 if (main_thread_blocking_on_job_ == job) {
317 main_thread_blocking_on_job_ = nullptr;
318 main_thread_blocking_signal_.NotifyOne();
319 }
320 }
321 // Don't touch |this| anymore after this point, as it might have been
322 // deleted.
199 } 323 }
200 324
201 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { 325 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
202 idle_task_scheduled_ = false; 326 {
327 base::LockGuard<base::Mutex> lock(&mutex_);
328 idle_task_scheduled_ = false;
329 }
203 330
204 // Number of jobs that are unlikely to make progress during any idle callback 331 // Number of jobs that are unlikely to make progress during any idle callback
205 // due to their estimated duration. 332 // due to their estimated duration.
206 size_t too_long_jobs = 0; 333 size_t too_long_jobs = 0;
207 334
208 // Iterate over all available jobs & remaining time. For each job, decide 335 // Iterate over all available jobs & remaining time. For each job, decide
209 // whether to 1) skip it (if it would take too long), 2) erase it (if it's 336 // whether to 1) skip it (if it would take too long), 2) erase it (if it's
210 // finished), or 3) make progress on it. 337 // finished), or 3) make progress on it.
211 double idle_time_in_seconds = 338 double idle_time_in_seconds =
212 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); 339 deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
213 for (auto job = jobs_.begin(); 340 for (auto job = jobs_.begin();
214 job != jobs_.end() && idle_time_in_seconds > 0.0; 341 job != jobs_.end() && idle_time_in_seconds > 0.0;
215 idle_time_in_seconds = 342 idle_time_in_seconds =
216 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { 343 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
344 // Don't work on jobs that are being worked on by background tasks.
345 // Similarly, remove jobs we work on from the set of available background
346 // jobs.
347 std::unique_ptr<base::LockGuard<base::Mutex>> lock(
348 new base::LockGuard<base::Mutex>(&mutex_));
349 if (running_background_jobs_.find(job->second.get()) !=
350 running_background_jobs_.end()) {
351 ++job;
352 continue;
353 }
354 auto it = pending_background_jobs_.find(job->second.get());
217 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); 355 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
218 if (idle_time_in_seconds < 356 if (idle_time_in_seconds <
219 (estimate_in_ms / 357 (estimate_in_ms /
220 static_cast<double>(base::Time::kMillisecondsPerSecond))) { 358 static_cast<double>(base::Time::kMillisecondsPerSecond))) {
221 // If there's not enough time left, try to estimate whether we would 359 // If there's not enough time left, try to estimate whether we would
222 // have managed to finish the job in a large idle task to assess 360 // have managed to finish the job in a large idle task to assess
223 // whether we should ask for another idle callback. 361 // whether we should ask for another idle callback.
224 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; 362 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs;
363 if (it == pending_background_jobs_.end()) {
364 lock.reset();
365 ConsiderJobForBackgroundProcessing(job->second.get());
366 }
225 ++job; 367 ++job;
226 } else if (IsFinished(job->second.get())) { 368 } else if (IsFinished(job->second.get())) {
369 DCHECK(it == pending_background_jobs_.end());
227 job->second->ResetOnMainThread(); 370 job->second->ResetOnMainThread();
228 job = jobs_.erase(job); 371 job = jobs_.erase(job);
229 break; 372 continue;
230 } else { 373 } else {
231 // Do one step, and keep processing the job (as we don't advance the 374 // Do one step, and keep processing the job (as we don't advance the
232 // iterator). 375 // iterator).
376 if (it != pending_background_jobs_.end()) {
377 pending_background_jobs_.erase(it);
378 }
379 lock.reset();
233 DoNextStepOnMainThread(isolate_, job->second.get(), 380 DoNextStepOnMainThread(isolate_, job->second.get(),
234 ExceptionHandling::kSwallow); 381 ExceptionHandling::kSwallow);
235 } 382 }
236 } 383 }
237 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); 384 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded();
238 } 385 }
239 386
240 } // namespace internal 387 } // namespace internal
241 } // namespace v8 388 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler-dispatcher/compiler-dispatcher.h ('k') | test/unittests/compiler-dispatcher/compiler-dispatcher-unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698