LCOV - code coverage report
Current view: top level - src/compiler-dispatcher - compiler-dispatcher.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 132 151 87.4 %
Date: 2019-01-20 Functions: 20 21 95.2 %

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

Generated by: LCOV version 1.10