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
3 // found in the LICENSE file.
4
5 #include "src/compiler-dispatcher/compiler-dispatcher.h"
6
7 #include "include/v8-platform.h"
8 #include "include/v8.h"
9 #include "src/base/platform/time.h"
10 #include "src/cancelable-task.h"
11 #include "src/compilation-info.h"
12 #include "src/compiler-dispatcher/compiler-dispatcher-job.h"
13 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
14 #include "src/flags.h"
15 #include "src/objects-inl.h"
16
17 namespace v8 {
18 namespace internal {
19
20 namespace {
21
22 enum class ExceptionHandling { kSwallow, kThrow };
23
DoNextStepOnMainThread(Isolate * isolate,CompilerDispatcherJob * job,ExceptionHandling exception_handling)24 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job,
25 ExceptionHandling exception_handling) {
26 DCHECK(ThreadId::Current().Equals(isolate->thread_id()));
27 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
28 "V8.CompilerDispatcherForgroundStep");
29
30 // Ensure we are in the correct context for the job.
31 SaveContext save(isolate);
32 isolate->set_context(job->context());
33
34 switch (job->status()) {
35 case CompileJobStatus::kInitial:
36 job->PrepareToParseOnMainThread();
37 break;
38
39 case CompileJobStatus::kReadyToParse:
40 job->Parse();
41 break;
42
43 case CompileJobStatus::kParsed:
44 job->FinalizeParsingOnMainThread();
45 break;
46
47 case CompileJobStatus::kReadyToAnalyze:
48 job->AnalyzeOnMainThread();
49 break;
50
51 case CompileJobStatus::kAnalyzed:
52 job->PrepareToCompileOnMainThread();
53 break;
54
55 case CompileJobStatus::kReadyToCompile:
56 job->Compile();
57 break;
58
59 case CompileJobStatus::kCompiled:
60 job->FinalizeCompilingOnMainThread();
61 break;
62
63 case CompileJobStatus::kFailed:
64 case CompileJobStatus::kDone:
65 break;
66 }
67
68 DCHECK_EQ(job->status() == CompileJobStatus::kFailed,
69 isolate->has_pending_exception());
70 if (job->status() == CompileJobStatus::kFailed &&
71 exception_handling == ExceptionHandling::kSwallow) {
72 isolate->clear_pending_exception();
73 }
74 return job->status() != CompileJobStatus::kFailed;
75 }
76
IsFinished(CompilerDispatcherJob * job)77 bool IsFinished(CompilerDispatcherJob* job) {
78 return job->status() == CompileJobStatus::kDone ||
79 job->status() == CompileJobStatus::kFailed;
80 }
81
CanRunOnAnyThread(CompilerDispatcherJob * job)82 bool CanRunOnAnyThread(CompilerDispatcherJob* job) {
83 return job->status() == CompileJobStatus::kReadyToParse ||
84 job->status() == CompileJobStatus::kReadyToCompile;
85 }
86
DoNextStepOnBackgroundThread(CompilerDispatcherJob * job)87 void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) {
88 DCHECK(CanRunOnAnyThread(job));
89 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
90 "V8.CompilerDispatcherBackgroundStep");
91
92 switch (job->status()) {
93 case CompileJobStatus::kReadyToParse:
94 job->Parse();
95 break;
96
97 case CompileJobStatus::kReadyToCompile:
98 job->Compile();
99 break;
100
101 default:
102 UNREACHABLE();
103 }
104 }
105
106 // Theoretically we get 50ms of idle time max, however it's unlikely that
107 // we'll get all of it so try to be a conservative.
108 const double kMaxIdleTimeToExpectInMs = 40;
109
110 class MemoryPressureTask : public CancelableTask {
111 public:
112 MemoryPressureTask(Isolate* isolate, CancelableTaskManager* task_manager,
113 CompilerDispatcher* dispatcher);
114 ~MemoryPressureTask() override;
115
116 // CancelableTask implementation.
117 void RunInternal() override;
118
119 private:
120 CompilerDispatcher* dispatcher_;
121
122 DISALLOW_COPY_AND_ASSIGN(MemoryPressureTask);
123 };
124
MemoryPressureTask(Isolate * isolate,CancelableTaskManager * task_manager,CompilerDispatcher * dispatcher)125 MemoryPressureTask::MemoryPressureTask(Isolate* isolate,
126 CancelableTaskManager* task_manager,
127 CompilerDispatcher* dispatcher)
128 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {}
129
~MemoryPressureTask()130 MemoryPressureTask::~MemoryPressureTask() {}
131
RunInternal()132 void MemoryPressureTask::RunInternal() {
133 dispatcher_->AbortAll(CompilerDispatcher::BlockingBehavior::kDontBlock);
134 }
135
136 } // namespace
137
138 class CompilerDispatcher::AbortTask : public CancelableTask {
139 public:
140 AbortTask(Isolate* isolate, CancelableTaskManager* task_manager,
141 CompilerDispatcher* dispatcher);
142 ~AbortTask() override;
143
144 // CancelableTask implementation.
145 void RunInternal() override;
146
147 private:
148 CompilerDispatcher* dispatcher_;
149
150 DISALLOW_COPY_AND_ASSIGN(AbortTask);
151 };
152
AbortTask(Isolate * isolate,CancelableTaskManager * task_manager,CompilerDispatcher * dispatcher)153 CompilerDispatcher::AbortTask::AbortTask(Isolate* isolate,
154 CancelableTaskManager* task_manager,
155 CompilerDispatcher* dispatcher)
156 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {}
157
~AbortTask()158 CompilerDispatcher::AbortTask::~AbortTask() {}
159
RunInternal()160 void CompilerDispatcher::AbortTask::RunInternal() {
161 dispatcher_->AbortInactiveJobs();
162 }
163
164 class CompilerDispatcher::BackgroundTask : public CancelableTask {
165 public:
166 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager,
167 CompilerDispatcher* dispatcher);
168 ~BackgroundTask() override;
169
170 // CancelableTask implementation.
171 void RunInternal() override;
172
173 private:
174 CompilerDispatcher* dispatcher_;
175
176 DISALLOW_COPY_AND_ASSIGN(BackgroundTask);
177 };
178
BackgroundTask(Isolate * isolate,CancelableTaskManager * task_manager,CompilerDispatcher * dispatcher)179 CompilerDispatcher::BackgroundTask::BackgroundTask(
180 Isolate* isolate, CancelableTaskManager* task_manager,
181 CompilerDispatcher* dispatcher)
182 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {}
183
~BackgroundTask()184 CompilerDispatcher::BackgroundTask::~BackgroundTask() {}
185
RunInternal()186 void CompilerDispatcher::BackgroundTask::RunInternal() {
187 dispatcher_->DoBackgroundWork();
188 }
189
190 class CompilerDispatcher::IdleTask : public CancelableIdleTask {
191 public:
192 IdleTask(Isolate* isolate, CancelableTaskManager* task_manager,
193 CompilerDispatcher* dispatcher);
194 ~IdleTask() override;
195
196 // CancelableIdleTask implementation.
197 void RunInternal(double deadline_in_seconds) override;
198
199 private:
200 CompilerDispatcher* dispatcher_;
201
202 DISALLOW_COPY_AND_ASSIGN(IdleTask);
203 };
204
IdleTask(Isolate * isolate,CancelableTaskManager * task_manager,CompilerDispatcher * dispatcher)205 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate,
206 CancelableTaskManager* task_manager,
207 CompilerDispatcher* dispatcher)
208 : CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {}
209
~IdleTask()210 CompilerDispatcher::IdleTask::~IdleTask() {}
211
RunInternal(double deadline_in_seconds)212 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) {
213 dispatcher_->DoIdleWork(deadline_in_seconds);
214 }
215
CompilerDispatcher(Isolate * isolate,Platform * platform,size_t max_stack_size)216 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
217 size_t max_stack_size)
218 : isolate_(isolate),
219 platform_(platform),
220 max_stack_size_(max_stack_size),
221 trace_compiler_dispatcher_(FLAG_trace_compiler_dispatcher),
222 tracer_(new CompilerDispatcherTracer(isolate_)),
223 task_manager_(new CancelableTaskManager()),
224 memory_pressure_level_(MemoryPressureLevel::kNone),
225 abort_(false),
226 idle_task_scheduled_(false),
227 num_scheduled_background_tasks_(0),
228 main_thread_blocking_on_job_(nullptr),
229 block_for_testing_(false),
230 semaphore_for_testing_(0) {
231 if (trace_compiler_dispatcher_ && !IsEnabled()) {
232 PrintF("CompilerDispatcher: dispatcher is disabled\n");
233 }
234 }
235
~CompilerDispatcher()236 CompilerDispatcher::~CompilerDispatcher() {
237 // To avoid crashing in unit tests due to unfished jobs.
238 AbortAll(BlockingBehavior::kBlock);
239 task_manager_->CancelAndWait();
240 }
241
CanEnqueue(Handle<SharedFunctionInfo> function)242 bool CompilerDispatcher::CanEnqueue(Handle<SharedFunctionInfo> function) {
243 if (!IsEnabled()) return false;
244
245 DCHECK(FLAG_ignition);
246
247 if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) {
248 return false;
249 }
250
251 {
252 base::LockGuard<base::Mutex> lock(&mutex_);
253 if (abort_) return false;
254 }
255
256 // We only handle functions (no eval / top-level code / wasm) that are
257 // attached to a script.
258 if (!function->script()->IsScript() || function->is_toplevel() ||
259 function->asm_function() || function->native()) {
260 return false;
261 }
262
263 return true;
264 }
265
Enqueue(Handle<SharedFunctionInfo> function)266 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
267 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
268 "V8.CompilerDispatcherEnqueue");
269 if (!CanEnqueue(function)) return false;
270 if (IsEnqueued(function)) return true;
271
272 if (trace_compiler_dispatcher_) {
273 PrintF("CompilerDispatcher: enqueuing ");
274 function->ShortPrint();
275 PrintF(" for parse and compile\n");
276 }
277
278 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
279 isolate_, tracer_.get(), function, max_stack_size_));
280 std::pair<int, int> key(Script::cast(function->script())->id(),
281 function->function_literal_id());
282 jobs_.insert(std::make_pair(key, std::move(job)));
283 ScheduleIdleTaskIfNeeded();
284 return true;
285 }
286
EnqueueAndStep(Handle<SharedFunctionInfo> function)287 bool CompilerDispatcher::EnqueueAndStep(Handle<SharedFunctionInfo> function) {
288 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
289 "V8.CompilerDispatcherEnqueueAndStep");
290 if (IsEnqueued(function)) return true;
291 if (!Enqueue(function)) return false;
292
293 if (trace_compiler_dispatcher_) {
294 PrintF("CompilerDispatcher: stepping ");
295 function->ShortPrint();
296 PrintF("\n");
297 }
298 JobMap::const_iterator job = GetJobFor(function);
299 DoNextStepOnMainThread(isolate_, job->second.get(),
300 ExceptionHandling::kSwallow);
301 ConsiderJobForBackgroundProcessing(job->second.get());
302 return true;
303 }
304
Enqueue(Handle<Script> script,Handle<SharedFunctionInfo> function,FunctionLiteral * literal,std::shared_ptr<Zone> parse_zone,std::shared_ptr<DeferredHandles> parse_handles,std::shared_ptr<DeferredHandles> compile_handles)305 bool CompilerDispatcher::Enqueue(
306 Handle<Script> script, Handle<SharedFunctionInfo> function,
307 FunctionLiteral* literal, std::shared_ptr<Zone> parse_zone,
308 std::shared_ptr<DeferredHandles> parse_handles,
309 std::shared_ptr<DeferredHandles> compile_handles) {
310 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
311 "V8.CompilerDispatcherEnqueue");
312 if (!CanEnqueue(function)) return false;
313 if (IsEnqueued(function)) return true;
314
315 if (trace_compiler_dispatcher_) {
316 PrintF("CompilerDispatcher: enqueuing ");
317 function->ShortPrint();
318 PrintF(" for compile\n");
319 }
320
321 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
322 isolate_, tracer_.get(), script, function, literal, parse_zone,
323 parse_handles, compile_handles, max_stack_size_));
324 std::pair<int, int> key(Script::cast(function->script())->id(),
325 function->function_literal_id());
326 jobs_.insert(std::make_pair(key, std::move(job)));
327 ScheduleIdleTaskIfNeeded();
328 return true;
329 }
330
EnqueueAndStep(Handle<Script> script,Handle<SharedFunctionInfo> function,FunctionLiteral * literal,std::shared_ptr<Zone> parse_zone,std::shared_ptr<DeferredHandles> parse_handles,std::shared_ptr<DeferredHandles> compile_handles)331 bool CompilerDispatcher::EnqueueAndStep(
332 Handle<Script> script, Handle<SharedFunctionInfo> function,
333 FunctionLiteral* literal, std::shared_ptr<Zone> parse_zone,
334 std::shared_ptr<DeferredHandles> parse_handles,
335 std::shared_ptr<DeferredHandles> compile_handles) {
336 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
337 "V8.CompilerDispatcherEnqueueAndStep");
338 if (IsEnqueued(function)) return true;
339 if (!Enqueue(script, function, literal, parse_zone, parse_handles,
340 compile_handles)) {
341 return false;
342 }
343
344 if (trace_compiler_dispatcher_) {
345 PrintF("CompilerDispatcher: stepping ");
346 function->ShortPrint();
347 PrintF("\n");
348 }
349 JobMap::const_iterator job = GetJobFor(function);
350 DoNextStepOnMainThread(isolate_, job->second.get(),
351 ExceptionHandling::kSwallow);
352 ConsiderJobForBackgroundProcessing(job->second.get());
353 return true;
354 }
355
IsEnabled() const356 bool CompilerDispatcher::IsEnabled() const { return FLAG_compiler_dispatcher; }
357
IsEnqueued(Handle<SharedFunctionInfo> function) const358 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
359 if (jobs_.empty()) return false;
360 return GetJobFor(function) != jobs_.end();
361 }
362
WaitForJobIfRunningOnBackground(CompilerDispatcherJob * job)363 void CompilerDispatcher::WaitForJobIfRunningOnBackground(
364 CompilerDispatcherJob* job) {
365 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
366 "V8.CompilerDispatcherWaitForBackgroundJob");
367 RuntimeCallTimerScope runtimeTimer(
368 isolate_, &RuntimeCallStats::CompileWaitForDispatcher);
369
370 base::LockGuard<base::Mutex> lock(&mutex_);
371 if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
372 pending_background_jobs_.erase(job);
373 return;
374 }
375 DCHECK_NULL(main_thread_blocking_on_job_);
376 main_thread_blocking_on_job_ = job;
377 while (main_thread_blocking_on_job_ != nullptr) {
378 main_thread_blocking_signal_.Wait(&mutex_);
379 }
380 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end());
381 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end());
382 }
383
FinishNow(Handle<SharedFunctionInfo> function)384 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
385 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
386 "V8.CompilerDispatcherFinishNow");
387 JobMap::const_iterator job = GetJobFor(function);
388 CHECK(job != jobs_.end());
389
390 if (trace_compiler_dispatcher_) {
391 PrintF("CompilerDispatcher: finishing ");
392 function->ShortPrint();
393 PrintF(" now\n");
394 }
395
396 WaitForJobIfRunningOnBackground(job->second.get());
397 while (!IsFinished(job->second.get())) {
398 DoNextStepOnMainThread(isolate_, job->second.get(),
399 ExceptionHandling::kThrow);
400 }
401 bool result = job->second->status() != CompileJobStatus::kFailed;
402
403 if (trace_compiler_dispatcher_) {
404 PrintF("CompilerDispatcher: finished working on ");
405 function->ShortPrint();
406 PrintF(": %s\n", result ? "success" : "failure");
407 tracer_->DumpStatistics();
408 }
409
410 job->second->ResetOnMainThread();
411 jobs_.erase(job);
412 if (jobs_.empty()) {
413 base::LockGuard<base::Mutex> lock(&mutex_);
414 abort_ = false;
415 }
416 return result;
417 }
418
AbortAll(BlockingBehavior blocking)419 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
420 bool background_tasks_running =
421 task_manager_->TryAbortAll() == CancelableTaskManager::kTaskRunning;
422 if (!background_tasks_running || blocking == BlockingBehavior::kBlock) {
423 for (auto& it : jobs_) {
424 WaitForJobIfRunningOnBackground(it.second.get());
425 if (trace_compiler_dispatcher_) {
426 PrintF("CompilerDispatcher: aborted ");
427 it.second->ShortPrint();
428 PrintF("\n");
429 }
430 it.second->ResetOnMainThread();
431 }
432 jobs_.clear();
433 {
434 base::LockGuard<base::Mutex> lock(&mutex_);
435 DCHECK(pending_background_jobs_.empty());
436 DCHECK(running_background_jobs_.empty());
437 abort_ = false;
438 }
439 return;
440 }
441
442 {
443 base::LockGuard<base::Mutex> lock(&mutex_);
444 abort_ = true;
445 pending_background_jobs_.clear();
446 }
447 AbortInactiveJobs();
448
449 // All running background jobs might already have scheduled idle tasks instead
450 // of abort tasks. Schedule a single abort task here to make sure they get
451 // processed as soon as possible (and not first when we have idle time).
452 ScheduleAbortTask();
453 }
454
AbortInactiveJobs()455 void CompilerDispatcher::AbortInactiveJobs() {
456 {
457 base::LockGuard<base::Mutex> lock(&mutex_);
458 // Since we schedule two abort tasks per async abort, we might end up
459 // here with nothing left to do.
460 if (!abort_) return;
461 }
462 for (auto it = jobs_.begin(); it != jobs_.end();) {
463 auto job = it;
464 ++it;
465 {
466 base::LockGuard<base::Mutex> lock(&mutex_);
467 if (running_background_jobs_.find(job->second.get()) !=
468 running_background_jobs_.end()) {
469 continue;
470 }
471 }
472 if (trace_compiler_dispatcher_) {
473 PrintF("CompilerDispatcher: aborted ");
474 job->second->ShortPrint();
475 PrintF("\n");
476 }
477 job->second->ResetOnMainThread();
478 jobs_.erase(job);
479 }
480 if (jobs_.empty()) {
481 base::LockGuard<base::Mutex> lock(&mutex_);
482 abort_ = false;
483 }
484 }
485
MemoryPressureNotification(v8::MemoryPressureLevel level,bool is_isolate_locked)486 void CompilerDispatcher::MemoryPressureNotification(
487 v8::MemoryPressureLevel level, bool is_isolate_locked) {
488 MemoryPressureLevel previous = memory_pressure_level_.Value();
489 memory_pressure_level_.SetValue(level);
490 // If we're already under pressure, we haven't accepted new tasks meanwhile
491 // and can just return. If we're no longer under pressure, we're also done.
492 if (previous != MemoryPressureLevel::kNone ||
493 level == MemoryPressureLevel::kNone) {
494 return;
495 }
496 if (trace_compiler_dispatcher_) {
497 PrintF("CompilerDispatcher: received memory pressure notification\n");
498 }
499 if (is_isolate_locked) {
500 AbortAll(BlockingBehavior::kDontBlock);
501 } else {
502 {
503 base::LockGuard<base::Mutex> lock(&mutex_);
504 if (abort_) return;
505 // By going into abort mode here, and clearing the
506 // pending_background_jobs_, we at keep existing background jobs from
507 // picking up more work before the MemoryPressureTask gets executed.
508 abort_ = true;
509 pending_background_jobs_.clear();
510 }
511 platform_->CallOnForegroundThread(
512 reinterpret_cast<v8::Isolate*>(isolate_),
513 new MemoryPressureTask(isolate_, task_manager_.get(), this));
514 }
515 }
516
GetJobFor(Handle<SharedFunctionInfo> shared) const517 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
518 Handle<SharedFunctionInfo> shared) const {
519 if (!shared->script()->IsScript()) return jobs_.end();
520 std::pair<int, int> key(Script::cast(shared->script())->id(),
521 shared->function_literal_id());
522 auto range = jobs_.equal_range(key);
523 for (auto job = range.first; job != range.second; ++job) {
524 if (job->second->IsAssociatedWith(shared)) return job;
525 }
526 return jobs_.end();
527 }
528
ScheduleIdleTaskFromAnyThread()529 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() {
530 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
531 DCHECK(platform_->IdleTasksEnabled(v8_isolate));
532 {
533 base::LockGuard<base::Mutex> lock(&mutex_);
534 if (idle_task_scheduled_) return;
535 idle_task_scheduled_ = true;
536 }
537 platform_->CallIdleOnForegroundThread(
538 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this));
539 }
540
ScheduleIdleTaskIfNeeded()541 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
542 if (jobs_.empty()) return;
543 ScheduleIdleTaskFromAnyThread();
544 }
545
ScheduleAbortTask()546 void CompilerDispatcher::ScheduleAbortTask() {
547 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
548 platform_->CallOnForegroundThread(
549 v8_isolate, new AbortTask(isolate_, task_manager_.get(), this));
550 }
551
ConsiderJobForBackgroundProcessing(CompilerDispatcherJob * job)552 void CompilerDispatcher::ConsiderJobForBackgroundProcessing(
553 CompilerDispatcherJob* job) {
554 if (!CanRunOnAnyThread(job)) return;
555 {
556 base::LockGuard<base::Mutex> lock(&mutex_);
557 pending_background_jobs_.insert(job);
558 }
559 ScheduleMoreBackgroundTasksIfNeeded();
560 }
561
ScheduleMoreBackgroundTasksIfNeeded()562 void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() {
563 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
564 "V8.CompilerDispatcherScheduleMoreBackgroundTasksIfNeeded");
565 if (FLAG_single_threaded) return;
566 {
567 base::LockGuard<base::Mutex> lock(&mutex_);
568 if (pending_background_jobs_.empty()) return;
569 if (platform_->NumberOfAvailableBackgroundThreads() <=
570 num_scheduled_background_tasks_) {
571 return;
572 }
573 ++num_scheduled_background_tasks_;
574 }
575 platform_->CallOnBackgroundThread(
576 new BackgroundTask(isolate_, task_manager_.get(), this),
577 v8::Platform::kShortRunningTask);
578 }
579
DoBackgroundWork()580 void CompilerDispatcher::DoBackgroundWork() {
581 CompilerDispatcherJob* job = nullptr;
582 {
583 base::LockGuard<base::Mutex> lock(&mutex_);
584 --num_scheduled_background_tasks_;
585 if (!pending_background_jobs_.empty()) {
586 auto it = pending_background_jobs_.begin();
587 job = *it;
588 pending_background_jobs_.erase(it);
589 running_background_jobs_.insert(job);
590 }
591 }
592 if (job == nullptr) return;
593
594 if (V8_UNLIKELY(block_for_testing_.Value())) {
595 block_for_testing_.SetValue(false);
596 semaphore_for_testing_.Wait();
597 }
598
599 if (trace_compiler_dispatcher_) {
600 PrintF("CompilerDispatcher: doing background work\n");
601 }
602
603 DoNextStepOnBackgroundThread(job);
604
605 ScheduleMoreBackgroundTasksIfNeeded();
606 // Unconditionally schedule an idle task, as all background steps have to be
607 // followed by a main thread step.
608 ScheduleIdleTaskFromAnyThread();
609
610 {
611 base::LockGuard<base::Mutex> lock(&mutex_);
612 running_background_jobs_.erase(job);
613
614 if (running_background_jobs_.empty() && abort_) {
615 // This is the last background job that finished. The abort task
616 // scheduled by AbortAll might already have ran, so schedule another
617 // one to be on the safe side.
618 ScheduleAbortTask();
619 }
620
621 if (main_thread_blocking_on_job_ == job) {
622 main_thread_blocking_on_job_ = nullptr;
623 main_thread_blocking_signal_.NotifyOne();
624 }
625 }
626 // Don't touch |this| anymore after this point, as it might have been
627 // deleted.
628 }
629
DoIdleWork(double deadline_in_seconds)630 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
631 bool aborted = false;
632 {
633 base::LockGuard<base::Mutex> lock(&mutex_);
634 idle_task_scheduled_ = false;
635 aborted = abort_;
636 }
637
638 if (aborted) {
639 AbortInactiveJobs();
640 return;
641 }
642
643 // Number of jobs that are unlikely to make progress during any idle callback
644 // due to their estimated duration.
645 size_t too_long_jobs = 0;
646
647 // Iterate over all available jobs & remaining time. For each job, decide
648 // whether to 1) skip it (if it would take too long), 2) erase it (if it's
649 // finished), or 3) make progress on it.
650 double idle_time_in_seconds =
651 deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
652
653 if (trace_compiler_dispatcher_) {
654 PrintF("CompilerDispatcher: received %0.1lfms of idle time\n",
655 idle_time_in_seconds *
656 static_cast<double>(base::Time::kMillisecondsPerSecond));
657 }
658 for (auto job = jobs_.begin();
659 job != jobs_.end() && idle_time_in_seconds > 0.0;
660 idle_time_in_seconds =
661 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
662 // Don't work on jobs that are being worked on by background tasks.
663 // Similarly, remove jobs we work on from the set of available background
664 // jobs.
665 std::unique_ptr<base::LockGuard<base::Mutex>> lock(
666 new base::LockGuard<base::Mutex>(&mutex_));
667 if (running_background_jobs_.find(job->second.get()) !=
668 running_background_jobs_.end()) {
669 ++job;
670 continue;
671 }
672 auto it = pending_background_jobs_.find(job->second.get());
673 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
674 if (idle_time_in_seconds <
675 (estimate_in_ms /
676 static_cast<double>(base::Time::kMillisecondsPerSecond))) {
677 // If there's not enough time left, try to estimate whether we would
678 // have managed to finish the job in a large idle task to assess
679 // whether we should ask for another idle callback.
680 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs;
681 if (it == pending_background_jobs_.end()) {
682 lock.reset();
683 ConsiderJobForBackgroundProcessing(job->second.get());
684 }
685 ++job;
686 } else if (IsFinished(job->second.get())) {
687 DCHECK(it == pending_background_jobs_.end());
688 if (trace_compiler_dispatcher_) {
689 PrintF("CompilerDispatcher: finished working on ");
690 job->second->ShortPrint();
691 PrintF(": %s\n", job->second->status() == CompileJobStatus::kDone
692 ? "success"
693 : "failure");
694 tracer_->DumpStatistics();
695 }
696 job->second->ResetOnMainThread();
697 job = jobs_.erase(job);
698 continue;
699 } else {
700 // Do one step, and keep processing the job (as we don't advance the
701 // iterator).
702 if (it != pending_background_jobs_.end()) {
703 pending_background_jobs_.erase(it);
704 }
705 lock.reset();
706 DoNextStepOnMainThread(isolate_, job->second.get(),
707 ExceptionHandling::kSwallow);
708 }
709 }
710 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded();
711 }
712
713 } // namespace internal
714 } // namespace v8
715