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
13namespace v8 {
14namespace internal {
15
16namespace {
17
18void 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
33class 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
78OptimizingCompileDispatcher::~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
89CompilationJob* 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
106void 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
122void 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
137void 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
152void 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
172void 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
198void 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
216void 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