Home | History | Annotate | Download | only in src
      1 // Copyright 2012 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/optimizing-compile-dispatcher.h"
      6 
      7 #include "src/base/atomicops.h"
      8 #include "src/full-codegen/full-codegen.h"
      9 #include "src/isolate.h"
     10 #include "src/tracing/trace-event.h"
     11 #include "src/v8.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 namespace {
     17 
     18 void DisposeCompilationJob(CompilationJob* job, bool restore_function_code) {
     19   if (restore_function_code) {
     20     Handle<JSFunction> function = job->info()->closure();
     21     function->ReplaceCode(function->shared()->code());
     22     // TODO(mvstanton): We can't call ensureliterals here due to allocation,
     23     // but we probably shouldn't call ReplaceCode either, as this
     24     // sometimes runs on the worker thread!
     25     // JSFunction::EnsureLiterals(function);
     26   }
     27   delete job;
     28 }
     29 
     30 }  // namespace
     31 
     32 
     33 class OptimizingCompileDispatcher::CompileTask : public v8::Task {
     34  public:
     35   explicit CompileTask(Isolate* isolate) : isolate_(isolate) {
     36     OptimizingCompileDispatcher* dispatcher =
     37         isolate_->optimizing_compile_dispatcher();
     38     base::LockGuard<base::Mutex> lock_guard(&dispatcher->ref_count_mutex_);
     39     ++dispatcher->ref_count_;
     40   }
     41 
     42   virtual ~CompileTask() {}
     43 
     44  private:
     45   // v8::Task overrides.
     46   void Run() override {
     47     DisallowHeapAllocation no_allocation;
     48     DisallowHandleAllocation no_handles;
     49     DisallowHandleDereference no_deref;
     50 
     51     OptimizingCompileDispatcher* dispatcher =
     52         isolate_->optimizing_compile_dispatcher();
     53     {
     54       TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
     55       TRACE_EVENT0("v8", "V8.RecompileConcurrent");
     56 
     57       if (dispatcher->recompilation_delay_ != 0) {
     58         base::OS::Sleep(base::TimeDelta::FromMilliseconds(
     59             dispatcher->recompilation_delay_));
     60       }
     61 
     62       dispatcher->CompileNext(dispatcher->NextInput(true));
     63     }
     64     {
     65       base::LockGuard<base::Mutex> lock_guard(&dispatcher->ref_count_mutex_);
     66       if (--dispatcher->ref_count_ == 0) {
     67         dispatcher->ref_count_zero_.NotifyOne();
     68       }
     69     }
     70   }
     71 
     72   Isolate* isolate_;
     73 
     74   DISALLOW_COPY_AND_ASSIGN(CompileTask);
     75 };
     76 
     77 
     78 OptimizingCompileDispatcher::~OptimizingCompileDispatcher() {
     79 #ifdef DEBUG
     80   {
     81     base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
     82     DCHECK_EQ(0, ref_count_);
     83   }
     84 #endif
     85   DCHECK_EQ(0, input_queue_length_);
     86   DeleteArray(input_queue_);
     87 }
     88 
     89 CompilationJob* OptimizingCompileDispatcher::NextInput(bool check_if_flushing) {
     90   base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
     91   if (input_queue_length_ == 0) return NULL;
     92   CompilationJob* job = input_queue_[InputQueueIndex(0)];
     93   DCHECK_NOT_NULL(job);
     94   input_queue_shift_ = InputQueueIndex(1);
     95   input_queue_length_--;
     96   if (check_if_flushing) {
     97     if (static_cast<ModeFlag>(base::Acquire_Load(&mode_)) == FLUSH) {
     98       AllowHandleDereference allow_handle_dereference;
     99       DisposeCompilationJob(job, true);
    100       return NULL;
    101     }
    102   }
    103   return job;
    104 }
    105 
    106 void OptimizingCompileDispatcher::CompileNext(CompilationJob* job) {
    107   if (!job) return;
    108 
    109   // The function may have already been optimized by OSR.  Simply continue.
    110   CompilationJob::Status status = job->OptimizeGraph();
    111   USE(status);  // Prevent an unused-variable error.
    112 
    113   // The function may have already been optimized by OSR.  Simply continue.
    114   // Use a mutex to make sure that functions marked for install
    115   // are always also queued.
    116   base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
    117   output_queue_.push(job);
    118   isolate_->stack_guard()->RequestInstallCode();
    119 }
    120 
    121 
    122 void OptimizingCompileDispatcher::FlushOutputQueue(bool restore_function_code) {
    123   for (;;) {
    124     CompilationJob* job = NULL;
    125     {
    126       base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
    127       if (output_queue_.empty()) return;
    128       job = output_queue_.front();
    129       output_queue_.pop();
    130     }
    131 
    132     DisposeCompilationJob(job, restore_function_code);
    133   }
    134 }
    135 
    136 
    137 void OptimizingCompileDispatcher::Flush() {
    138   base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH));
    139   if (FLAG_block_concurrent_recompilation) Unblock();
    140   {
    141     base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
    142     while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
    143     base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE));
    144   }
    145   FlushOutputQueue(true);
    146   if (FLAG_trace_concurrent_recompilation) {
    147     PrintF("  ** Flushed concurrent recompilation queues.\n");
    148   }
    149 }
    150 
    151 
    152 void OptimizingCompileDispatcher::Stop() {
    153   base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH));
    154   if (FLAG_block_concurrent_recompilation) Unblock();
    155   {
    156     base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
    157     while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
    158     base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE));
    159   }
    160 
    161   if (recompilation_delay_ != 0) {
    162     // At this point the optimizing compiler thread's event loop has stopped.
    163     // There is no need for a mutex when reading input_queue_length_.
    164     while (input_queue_length_ > 0) CompileNext(NextInput());
    165     InstallOptimizedFunctions();
    166   } else {
    167     FlushOutputQueue(false);
    168   }
    169 }
    170 
    171 
    172 void OptimizingCompileDispatcher::InstallOptimizedFunctions() {
    173   HandleScope handle_scope(isolate_);
    174 
    175   for (;;) {
    176     CompilationJob* job = NULL;
    177     {
    178       base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
    179       if (output_queue_.empty()) return;
    180       job = output_queue_.front();
    181       output_queue_.pop();
    182     }
    183     CompilationInfo* info = job->info();
    184     Handle<JSFunction> function(*info->closure());
    185     if (function->IsOptimized()) {
    186       if (FLAG_trace_concurrent_recompilation) {
    187         PrintF("  ** Aborting compilation for ");
    188         function->ShortPrint();
    189         PrintF(" as it has already been optimized.\n");
    190       }
    191       DisposeCompilationJob(job, false);
    192     } else {
    193       Compiler::FinalizeCompilationJob(job);
    194     }
    195   }
    196 }
    197 
    198 void OptimizingCompileDispatcher::QueueForOptimization(CompilationJob* job) {
    199   DCHECK(IsQueueAvailable());
    200   {
    201     // Add job to the back of the input queue.
    202     base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
    203     DCHECK_LT(input_queue_length_, input_queue_capacity_);
    204     input_queue_[InputQueueIndex(input_queue_length_)] = job;
    205     input_queue_length_++;
    206   }
    207   if (FLAG_block_concurrent_recompilation) {
    208     blocked_jobs_++;
    209   } else {
    210     V8::GetCurrentPlatform()->CallOnBackgroundThread(
    211         new CompileTask(isolate_), v8::Platform::kShortRunningTask);
    212   }
    213 }
    214 
    215 
    216 void OptimizingCompileDispatcher::Unblock() {
    217   while (blocked_jobs_ > 0) {
    218     V8::GetCurrentPlatform()->CallOnBackgroundThread(
    219         new CompileTask(isolate_), v8::Platform::kShortRunningTask);
    220     blocked_jobs_--;
    221   }
    222 }
    223 
    224 
    225 }  // namespace internal
    226 }  // namespace v8
    227