LCOV - code coverage report
Current view: top level - src/compiler-dispatcher - compiler-dispatcher.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 131 146 89.7 %
Date: 2019-04-17 Functions: 19 20 95.0 %

          Line data    Source code
       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 "src/ast/ast.h"
       8             : #include "src/base/platform/time.h"
       9             : #include "src/base/template-utils.h"
      10             : #include "src/cancelable-task.h"
      11             : #include "src/compiler.h"
      12             : #include "src/flags.h"
      13             : #include "src/global-handles.h"
      14             : #include "src/objects-inl.h"
      15             : #include "src/parsing/parse-info.h"
      16             : #include "src/parsing/parser.h"
      17             : #include "src/task-utils.h"
      18             : #include "src/zone/zone-list-inl.h"  // crbug.com/v8/8816
      19             : 
      20             : namespace v8 {
      21             : namespace internal {
      22             : 
      23           0 : CompilerDispatcher::Job::Job(BackgroundCompileTask* task_arg)
      24          73 :     : task(task_arg), has_run(false), aborted(false) {}
      25             : 
      26             : CompilerDispatcher::Job::~Job() = default;
      27             : 
      28       62434 : CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
      29             :                                        size_t max_stack_size)
      30             :     : isolate_(isolate),
      31             :       allocator_(isolate->allocator()),
      32             :       worker_thread_runtime_call_stats_(
      33             :           isolate->counters()->worker_thread_runtime_call_stats()),
      34             :       background_compile_timer_(
      35             :           isolate->counters()->compile_function_on_background()),
      36             :       taskrunner_(platform->GetForegroundTaskRunner(
      37       62434 :           reinterpret_cast<v8::Isolate*>(isolate))),
      38             :       platform_(platform),
      39             :       max_stack_size_(max_stack_size),
      40             :       trace_compiler_dispatcher_(FLAG_trace_compiler_dispatcher),
      41             :       task_manager_(new CancelableTaskManager()),
      42             :       next_job_id_(0),
      43             :       shared_to_unoptimized_job_id_(isolate->heap()),
      44             :       idle_task_scheduled_(false),
      45             :       num_worker_tasks_(0),
      46             :       main_thread_blocking_on_job_(nullptr),
      47             :       block_for_testing_(false),
      48      499476 :       semaphore_for_testing_(0) {
      49       62435 :   if (trace_compiler_dispatcher_ && !IsEnabled()) {
      50           0 :     PrintF("CompilerDispatcher: dispatcher is disabled\n");
      51             :   }
      52       62435 : }
      53             : 
      54      312093 : CompilerDispatcher::~CompilerDispatcher() {
      55             :   // AbortAll must be called before CompilerDispatcher is destroyed.
      56       62418 :   CHECK(task_manager_->canceled());
      57       62420 : }
      58             : 
      59          73 : base::Optional<CompilerDispatcher::JobId> CompilerDispatcher::Enqueue(
      60             :     const ParseInfo* outer_parse_info, const AstRawString* function_name,
      61             :     const FunctionLiteral* function_literal) {
      62         219 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
      63             :                "V8.CompilerDispatcherEnqueue");
      64             :   RuntimeCallTimerScope runtimeTimer(
      65          73 :       isolate_, RuntimeCallCounterId::kCompileEnqueueOnDispatcher);
      66             : 
      67          73 :   if (!IsEnabled()) return base::nullopt;
      68             : 
      69             :   std::unique_ptr<Job> job = base::make_unique<Job>(new BackgroundCompileTask(
      70             :       allocator_, outer_parse_info, function_name, function_literal,
      71             :       worker_thread_runtime_call_stats_, background_compile_timer_,
      72         146 :       static_cast<int>(max_stack_size_)));
      73          73 :   JobMap::const_iterator it = InsertJob(std::move(job));
      74          73 :   JobId id = it->first;
      75          73 :   if (trace_compiler_dispatcher_) {
      76             :     PrintF("CompilerDispatcher: enqueued job %zu for function literal id %d\n",
      77           0 :            id, function_literal->function_literal_id());
      78             :   }
      79             : 
      80             :   // Post a a background worker task to perform the compilation on the worker
      81             :   // thread.
      82             :   {
      83          73 :     base::MutexGuard lock(&mutex_);
      84         146 :     pending_background_jobs_.insert(it->second.get());
      85             :   }
      86          73 :   ScheduleMoreWorkerTasksIfNeeded();
      87             :   return base::make_optional(id);
      88             : }
      89             : 
      90     2465910 : bool CompilerDispatcher::IsEnabled() const { return FLAG_compiler_dispatcher; }
      91             : 
      92      570612 : bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
      93      570612 :   if (jobs_.empty()) return false;
      94         150 :   return GetJobFor(function) != jobs_.end();
      95             : }
      96             : 
      97          10 : bool CompilerDispatcher::IsEnqueued(JobId job_id) const {
      98          10 :   return jobs_.find(job_id) != jobs_.end();
      99             : }
     100             : 
     101          66 : void CompilerDispatcher::RegisterSharedFunctionInfo(
     102             :     JobId job_id, SharedFunctionInfo function) {
     103             :   DCHECK_NE(jobs_.find(job_id), jobs_.end());
     104             : 
     105          66 :   if (trace_compiler_dispatcher_) {
     106           0 :     PrintF("CompilerDispatcher: registering ");
     107           0 :     function->ShortPrint();
     108           0 :     PrintF(" with job id %zu\n", job_id);
     109             :   }
     110             : 
     111             :   // Make a global handle to the function.
     112             :   Handle<SharedFunctionInfo> function_handle = Handle<SharedFunctionInfo>::cast(
     113          66 :       isolate_->global_handles()->Create(function));
     114             : 
     115             :   // Register mapping.
     116             :   auto job_it = jobs_.find(job_id);
     117             :   DCHECK_NE(job_it, jobs_.end());
     118             :   Job* job = job_it->second.get();
     119             :   shared_to_unoptimized_job_id_.Set(function_handle, job_id);
     120             : 
     121             :   {
     122          66 :     base::MutexGuard lock(&mutex_);
     123          66 :     job->function = function_handle;
     124          66 :     if (job->IsReadyToFinalize(lock)) {
     125             :       // Schedule an idle task to finalize job if it is ready.
     126          27 :       ScheduleIdleTaskFromAnyThread(lock);
     127             :     }
     128             :   }
     129          66 : }
     130             : 
     131          49 : void CompilerDispatcher::WaitForJobIfRunningOnBackground(Job* job) {
     132          99 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
     133             :                "V8.CompilerDispatcherWaitForBackgroundJob");
     134             :   RuntimeCallTimerScope runtimeTimer(
     135          49 :       isolate_, RuntimeCallCounterId::kCompileWaitForDispatcher);
     136             : 
     137          49 :   base::MutexGuard lock(&mutex_);
     138          49 :   if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
     139             :     pending_background_jobs_.erase(job);
     140             :     return;
     141             :   }
     142             :   DCHECK_NULL(main_thread_blocking_on_job_);
     143           1 :   main_thread_blocking_on_job_ = job;
     144           3 :   while (main_thread_blocking_on_job_ != nullptr) {
     145           1 :     main_thread_blocking_signal_.Wait(&mutex_);
     146             :   }
     147             :   DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end());
     148             :   DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end());
     149             : }
     150             : 
     151          46 : bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
     152         138 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
     153             :                "V8.CompilerDispatcherFinishNow");
     154             :   RuntimeCallTimerScope runtimeTimer(
     155          46 :       isolate_, RuntimeCallCounterId::kCompileFinishNowOnDispatcher);
     156          46 :   if (trace_compiler_dispatcher_) {
     157           0 :     PrintF("CompilerDispatcher: finishing ");
     158           0 :     function->ShortPrint();
     159           0 :     PrintF(" now\n");
     160             :   }
     161             : 
     162          46 :   JobMap::const_iterator it = GetJobFor(function);
     163          46 :   CHECK(it != jobs_.end());
     164             :   Job* job = it->second.get();
     165          46 :   WaitForJobIfRunningOnBackground(job);
     166             : 
     167          46 :   if (!job->has_run) {
     168          16 :     job->task->Run();
     169          16 :     job->has_run = true;
     170             :   }
     171             : 
     172             :   DCHECK(job->IsReadyToFinalize(&mutex_));
     173             :   DCHECK(!job->aborted);
     174          46 :   bool success = Compiler::FinalizeBackgroundCompileTask(
     175          46 :       job->task.get(), function, isolate_, Compiler::KEEP_EXCEPTION);
     176             : 
     177             :   DCHECK_NE(success, isolate_->has_pending_exception());
     178          46 :   RemoveJob(it);
     179          46 :   return success;
     180             : }
     181             : 
     182           7 : void CompilerDispatcher::AbortJob(JobId job_id) {
     183           7 :   if (trace_compiler_dispatcher_) {
     184           0 :     PrintF("CompilerDispatcher: aborted job %zu\n", job_id);
     185             :   }
     186             :   JobMap::const_iterator job_it = jobs_.find(job_id);
     187           7 :   Job* job = job_it->second.get();
     188             : 
     189           7 :   base::LockGuard<base::Mutex> lock(&mutex_);
     190             :   pending_background_jobs_.erase(job);
     191           7 :   if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
     192           6 :     RemoveJob(job_it);
     193             :   } else {
     194             :     // Job is currently running on the background thread, wait until it's done
     195             :     // and remove job then.
     196           1 :     job->aborted = true;
     197             :   }
     198           7 : }
     199             : 
     200       62420 : void CompilerDispatcher::AbortAll() {
     201       62420 :   task_manager_->TryAbortAll();
     202             : 
     203       62423 :   for (auto& it : jobs_) {
     204           3 :     WaitForJobIfRunningOnBackground(it.second.get());
     205           3 :     if (trace_compiler_dispatcher_) {
     206           0 :       PrintF("CompilerDispatcher: aborted job %zu\n", it.first);
     207             :     }
     208             :   }
     209             :   jobs_.clear();
     210             :   shared_to_unoptimized_job_id_.Clear();
     211             :   {
     212       62419 :     base::MutexGuard lock(&mutex_);
     213             :     DCHECK(pending_background_jobs_.empty());
     214             :     DCHECK(running_background_jobs_.empty());
     215             :   }
     216             : 
     217       62420 :   task_manager_->CancelAndWait();
     218       62419 : }
     219             : 
     220         121 : CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
     221             :     Handle<SharedFunctionInfo> shared) const {
     222             :   JobId* job_id_ptr = shared_to_unoptimized_job_id_.Find(shared);
     223             :   JobMap::const_iterator job = jobs_.end();
     224         121 :   if (job_id_ptr) {
     225             :     job = jobs_.find(*job_id_ptr);
     226             :   }
     227         121 :   return job;
     228             : }
     229             : 
     230          50 : void CompilerDispatcher::ScheduleIdleTaskFromAnyThread(
     231             :     const base::MutexGuard&) {
     232          50 :   if (!taskrunner_->IdleTasksEnabled()) return;
     233          50 :   if (idle_task_scheduled_) return;
     234             : 
     235          16 :   idle_task_scheduled_ = true;
     236          64 :   taskrunner_->PostIdleTask(MakeCancelableIdleTask(
     237             :       task_manager_.get(),
     238          48 :       [this](double deadline_in_seconds) { DoIdleWork(deadline_in_seconds); }));
     239             : }
     240             : 
     241          73 : void CompilerDispatcher::ScheduleMoreWorkerTasksIfNeeded() {
     242         204 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
     243             :                "V8.CompilerDispatcherScheduleMoreWorkerTasksIfNeeded");
     244             :   {
     245          73 :     base::MutexGuard lock(&mutex_);
     246          73 :     if (pending_background_jobs_.empty()) return;
     247          72 :     if (platform_->NumberOfWorkerThreads() <= num_worker_tasks_) {
     248             :       return;
     249             :     }
     250          58 :     ++num_worker_tasks_;
     251             :   }
     252         174 :   platform_->CallOnWorkerThread(
     253         333 :       MakeCancelableTask(task_manager_.get(), [this] { DoBackgroundWork(); }));
     254             : }
     255             : 
     256          43 : void CompilerDispatcher::DoBackgroundWork() {
     257         129 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
     258             :                "V8.CompilerDispatcherDoBackgroundWork");
     259          51 :   for (;;) {
     260          94 :     Job* job = nullptr;
     261             :     {
     262          94 :       base::MutexGuard lock(&mutex_);
     263          94 :       if (!pending_background_jobs_.empty()) {
     264             :         auto it = pending_background_jobs_.begin();
     265          51 :         job = *it;
     266             :         pending_background_jobs_.erase(it);
     267             :         running_background_jobs_.insert(job);
     268             :       }
     269             :     }
     270          94 :     if (job == nullptr) break;
     271             : 
     272          51 :     if (V8_UNLIKELY(block_for_testing_.Value())) {
     273             :       block_for_testing_.SetValue(false);
     274           1 :       semaphore_for_testing_.Wait();
     275             :     }
     276             : 
     277          51 :     if (trace_compiler_dispatcher_) {
     278           0 :       PrintF("CompilerDispatcher: doing background work\n");
     279             :     }
     280             : 
     281         102 :     job->task->Run();
     282             : 
     283             :     {
     284             :       base::MutexGuard lock(&mutex_);
     285             :       running_background_jobs_.erase(job);
     286             : 
     287          51 :       job->has_run = true;
     288          51 :       if (job->IsReadyToFinalize(lock)) {
     289             :         // Schedule an idle task to finalize the compilation on the main thread
     290             :         // if the job has a shared function info registered.
     291          21 :         ScheduleIdleTaskFromAnyThread(lock);
     292             :       }
     293             : 
     294          51 :       if (main_thread_blocking_on_job_ == job) {
     295           2 :         main_thread_blocking_on_job_ = nullptr;
     296           2 :         main_thread_blocking_signal_.NotifyOne();
     297             :       }
     298             :     }
     299             :   }
     300             : 
     301             :   {
     302             :     base::MutexGuard lock(&mutex_);
     303          43 :     --num_worker_tasks_;
     304             :   }
     305             :   // Don't touch |this| anymore after this point, as it might have been
     306             :   // deleted.
     307          43 : }
     308             : 
     309          16 : void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
     310          34 :   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
     311             :                "V8.CompilerDispatcherDoIdleWork");
     312             :   {
     313          16 :     base::MutexGuard lock(&mutex_);
     314          16 :     idle_task_scheduled_ = false;
     315             :   }
     316             : 
     317          16 :   if (trace_compiler_dispatcher_) {
     318           0 :     PrintF("CompilerDispatcher: received %0.1lfms of idle time\n",
     319           0 :            (deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) *
     320           0 :                static_cast<double>(base::Time::kMillisecondsPerSecond));
     321             :   }
     322          52 :   while (deadline_in_seconds > platform_->MonotonicallyIncreasingTime()) {
     323             :     // Find a job which is pending finalization and has a shared function info
     324             :     CompilerDispatcher::JobMap::const_iterator it;
     325             :     {
     326             :       base::MutexGuard lock(&mutex_);
     327          32 :       for (it = jobs_.cbegin(); it != jobs_.cend(); ++it) {
     328          18 :         if (it->second->IsReadyToFinalize(lock)) break;
     329             :       }
     330             :       // Since we hold the lock here, we can be sure no jobs have become ready
     331             :       // for finalization while we looped through the list.
     332          32 :       if (it == jobs_.cend()) return;
     333             : 
     334             :       DCHECK(it->second->IsReadyToFinalize(lock));
     335             :       DCHECK_EQ(running_background_jobs_.find(it->second.get()),
     336             :                 running_background_jobs_.end());
     337             :       DCHECK_EQ(pending_background_jobs_.find(it->second.get()),
     338             :                 pending_background_jobs_.end());
     339             :     }
     340             : 
     341             :     Job* job = it->second.get();
     342          18 :     if (!job->aborted) {
     343          17 :       Compiler::FinalizeBackgroundCompileTask(
     344             :           job->task.get(), job->function.ToHandleChecked(), isolate_,
     345          17 :           Compiler::CLEAR_EXCEPTION);
     346             :     }
     347          18 :     RemoveJob(it);
     348             :   }
     349             : 
     350             :   // We didn't return above so there still might be jobs to finalize.
     351             :   {
     352             :     base::MutexGuard lock(&mutex_);
     353           2 :     ScheduleIdleTaskFromAnyThread(lock);
     354             :   }
     355             : }
     356             : 
     357          73 : CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::InsertJob(
     358             :     std::unique_ptr<Job> job) {
     359             :   bool added;
     360             :   JobMap::const_iterator it;
     361             :   std::tie(it, added) =
     362         146 :       jobs_.insert(std::make_pair(next_job_id_++, std::move(job)));
     363             :   DCHECK(added);
     364          73 :   return it;
     365             : }
     366             : 
     367          70 : CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::RemoveJob(
     368             :     CompilerDispatcher::JobMap::const_iterator it) {
     369             :   Job* job = it->second.get();
     370             : 
     371             :   DCHECK_EQ(running_background_jobs_.find(job), running_background_jobs_.end());
     372             :   DCHECK_EQ(pending_background_jobs_.find(job), pending_background_jobs_.end());
     373             : 
     374             :   // Delete SFI associated with job if its been registered.
     375             :   Handle<SharedFunctionInfo> function;
     376          70 :   if (job->function.ToHandle(&function)) {
     377          63 :     GlobalHandles::Destroy(function.location());
     378             :   }
     379             : 
     380             :   // Delete job.
     381          70 :   return jobs_.erase(it);
     382             : }
     383             : 
     384             : }  // namespace internal
     385      121996 : }  // namespace v8

Generated by: LCOV version 1.10