• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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