5 #include "src/compiler-dispatcher/compiler-dispatcher.h" 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" 22 CompilerDispatcher::Job::Job(BackgroundCompileTask* task_arg)
23 : task(task_arg), has_run(false), aborted(false) {}
25 CompilerDispatcher::Job::~Job() =
default;
27 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
28 size_t max_stack_size)
30 allocator_(isolate->allocator()),
31 worker_thread_runtime_call_stats_(
32 isolate->counters()->worker_thread_runtime_call_stats()),
33 background_compile_timer_(
34 isolate->counters()->compile_function_on_background()),
35 taskrunner_(platform->GetForegroundTaskRunner(
36 reinterpret_cast<
v8::Isolate*>(isolate))),
38 max_stack_size_(max_stack_size),
39 trace_compiler_dispatcher_(FLAG_trace_compiler_dispatcher),
40 task_manager_(new CancelableTaskManager()),
42 shared_to_unoptimized_job_id_(isolate->heap()),
43 idle_task_scheduled_(false),
45 main_thread_blocking_on_job_(nullptr),
46 block_for_testing_(false),
47 semaphore_for_testing_(0) {
48 if (trace_compiler_dispatcher_ && !IsEnabled()) {
49 PrintF(
"CompilerDispatcher: dispatcher is disabled\n");
53 CompilerDispatcher::~CompilerDispatcher() {
55 CHECK(task_manager_->canceled());
58 base::Optional<CompilerDispatcher::JobId> CompilerDispatcher::Enqueue(
59 const ParseInfo* outer_parse_info,
const AstRawString* function_name,
60 const FunctionLiteral* function_literal) {
61 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(
"v8.compile"),
62 "V8.CompilerDispatcherEnqueue");
63 RuntimeCallTimerScope runtimeTimer(
64 isolate_, RuntimeCallCounterId::kCompileEnqueueOnDispatcher);
66 if (!IsEnabled())
return base::nullopt;
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 static_cast<int>(max_stack_size_)));
72 JobMap::const_iterator it = InsertJob(std::move(job));
74 if (trace_compiler_dispatcher_) {
75 PrintF(
"CompilerDispatcher: enqueued job %zu for function literal id %d\n",
76 id, function_literal->function_literal_id());
82 base::MutexGuard lock(&mutex_);
83 pending_background_jobs_.insert(it->second.get());
85 ScheduleMoreWorkerTasksIfNeeded();
86 return base::make_optional(
id);
89 bool CompilerDispatcher::IsEnabled()
const {
return FLAG_compiler_dispatcher; }
91 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo>
function)
const {
92 if (jobs_.empty())
return false;
93 return GetJobFor(
function) != jobs_.end();
96 bool CompilerDispatcher::IsEnqueued(JobId job_id)
const {
97 return jobs_.find(job_id) != jobs_.end();
100 void CompilerDispatcher::RegisterSharedFunctionInfo(
101 JobId job_id, SharedFunctionInfo*
function) {
102 DCHECK_NE(jobs_.find(job_id), jobs_.end());
104 if (trace_compiler_dispatcher_) {
105 PrintF(
"CompilerDispatcher: registering ");
106 function->ShortPrint();
107 PrintF(
" with job id %zu\n", job_id);
111 Handle<SharedFunctionInfo> function_handle =
112 isolate_->global_handles()->Create(
function);
115 auto job_it = jobs_.find(job_id);
116 DCHECK_NE(job_it, jobs_.end());
117 Job* job = job_it->second.get();
118 shared_to_unoptimized_job_id_.Set(function_handle, job_id);
121 base::MutexGuard lock(&mutex_);
122 job->function = function_handle;
123 if (job->IsReadyToFinalize(lock)) {
125 ScheduleIdleTaskFromAnyThread(lock);
130 void CompilerDispatcher::WaitForJobIfRunningOnBackground(Job* job) {
131 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(
"v8.compile"),
132 "V8.CompilerDispatcherWaitForBackgroundJob");
133 RuntimeCallTimerScope runtimeTimer(
134 isolate_, RuntimeCallCounterId::kCompileWaitForDispatcher);
136 base::MutexGuard lock(&mutex_);
137 if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
138 pending_background_jobs_.erase(job);
141 DCHECK_NULL(main_thread_blocking_on_job_);
142 main_thread_blocking_on_job_ = job;
143 while (main_thread_blocking_on_job_ !=
nullptr) {
144 main_thread_blocking_signal_.Wait(&mutex_);
146 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end());
147 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end());
150 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo>
function) {
151 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(
"v8.compile"),
152 "V8.CompilerDispatcherFinishNow");
153 RuntimeCallTimerScope runtimeTimer(
154 isolate_, RuntimeCallCounterId::kCompileFinishNowOnDispatcher);
155 if (trace_compiler_dispatcher_) {
156 PrintF(
"CompilerDispatcher: finishing ");
157 function->ShortPrint();
161 JobMap::const_iterator it = GetJobFor(
function);
162 CHECK(it != jobs_.end());
163 Job* job = it->second.get();
164 WaitForJobIfRunningOnBackground(job);
171 DCHECK(job->IsReadyToFinalize(&mutex_));
172 DCHECK(!job->aborted);
173 bool success = Compiler::FinalizeBackgroundCompileTask(
174 job->task.get(),
function, isolate_, Compiler::KEEP_EXCEPTION);
176 DCHECK_NE(success, isolate_->has_pending_exception());
181 void CompilerDispatcher::AbortJob(JobId job_id) {
182 if (trace_compiler_dispatcher_) {
183 PrintF(
"CompilerDispatcher: aborted job %zu\n", job_id);
185 JobMap::const_iterator job_it = jobs_.find(job_id);
186 Job* job = job_it->second.get();
188 base::LockGuard<base::Mutex> lock(&mutex_);
189 pending_background_jobs_.erase(job);
190 if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
199 void CompilerDispatcher::AbortAll() {
200 task_manager_->TryAbortAll();
202 for (
auto& it : jobs_) {
203 WaitForJobIfRunningOnBackground(it.second.get());
204 if (trace_compiler_dispatcher_) {
205 PrintF(
"CompilerDispatcher: aborted job %zu\n", it.first);
209 shared_to_unoptimized_job_id_.Clear();
211 base::MutexGuard lock(&mutex_);
212 DCHECK(pending_background_jobs_.empty());
213 DCHECK(running_background_jobs_.empty());
216 task_manager_->CancelAndWait();
219 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
220 Handle<SharedFunctionInfo> shared)
const {
221 JobId* job_id_ptr = shared_to_unoptimized_job_id_.Find(shared);
222 JobMap::const_iterator job = jobs_.end();
224 job = jobs_.find(*job_id_ptr);
229 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread(
230 const base::MutexGuard&) {
231 if (!taskrunner_->IdleTasksEnabled())
return;
232 if (idle_task_scheduled_)
return;
234 idle_task_scheduled_ =
true;
235 taskrunner_->PostIdleTask(MakeCancelableIdleTask(
237 [
this](
double deadline_in_seconds) { DoIdleWork(deadline_in_seconds); }));
240 void CompilerDispatcher::ScheduleMoreWorkerTasksIfNeeded() {
241 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(
"v8.compile"),
242 "V8.CompilerDispatcherScheduleMoreWorkerTasksIfNeeded");
244 base::MutexGuard lock(&mutex_);
245 if (pending_background_jobs_.empty())
return;
246 if (platform_->NumberOfWorkerThreads() <= num_worker_tasks_) {
251 platform_->CallOnWorkerThread(
252 MakeCancelableTask(task_manager_.get(), [
this] { DoBackgroundWork(); }));
255 void CompilerDispatcher::DoBackgroundWork() {
256 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(
"v8.compile"),
257 "V8.CompilerDispatcherDoBackgroundWork");
261 base::MutexGuard lock(&mutex_);
262 if (!pending_background_jobs_.empty()) {
263 auto it = pending_background_jobs_.begin();
265 pending_background_jobs_.erase(it);
266 running_background_jobs_.insert(job);
269 if (job ==
nullptr)
break;
271 if (V8_UNLIKELY(block_for_testing_.Value())) {
272 block_for_testing_.SetValue(
false);
273 semaphore_for_testing_.Wait();
276 if (trace_compiler_dispatcher_) {
277 PrintF(
"CompilerDispatcher: doing background work\n");
283 base::MutexGuard lock(&mutex_);
284 running_background_jobs_.erase(job);
287 if (job->IsReadyToFinalize(lock)) {
290 ScheduleIdleTaskFromAnyThread(lock);
293 if (main_thread_blocking_on_job_ == job) {
294 main_thread_blocking_on_job_ =
nullptr;
295 main_thread_blocking_signal_.NotifyOne();
301 base::MutexGuard lock(&mutex_);
308 void CompilerDispatcher::DoIdleWork(
double deadline_in_seconds) {
309 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT(
"v8.compile"),
310 "V8.CompilerDispatcherDoIdleWork");
312 base::MutexGuard lock(&mutex_);
313 idle_task_scheduled_ =
false;
316 if (trace_compiler_dispatcher_) {
317 PrintF(
"CompilerDispatcher: received %0.1lfms of idle time\n",
318 (deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) *
319 static_cast<double>(base::Time::kMillisecondsPerSecond));
321 while (deadline_in_seconds > platform_->MonotonicallyIncreasingTime()) {
323 CompilerDispatcher::JobMap::const_iterator it;
325 base::MutexGuard lock(&mutex_);
326 for (it = jobs_.cbegin(); it != jobs_.cend(); ++it) {
327 if (it->second->IsReadyToFinalize(lock))
break;
331 if (it == jobs_.cend())
return;
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());
340 Job* job = it->second.get();
342 Compiler::FinalizeBackgroundCompileTask(
343 job->task.get(), job->function.ToHandleChecked(), isolate_,
344 Compiler::CLEAR_EXCEPTION);
351 base::MutexGuard lock(&mutex_);
352 ScheduleIdleTaskFromAnyThread(lock);
356 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::InsertJob(
357 std::unique_ptr<Job> job) {
359 JobMap::const_iterator it;
360 std::tie(it, added) =
361 jobs_.insert(std::make_pair(next_job_id_++, std::move(job)));
366 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::RemoveJob(
367 CompilerDispatcher::JobMap::const_iterator it) {
368 Job* job = it->second.get();
370 DCHECK_EQ(running_background_jobs_.find(job), running_background_jobs_.end());
371 DCHECK_EQ(pending_background_jobs_.find(job), pending_background_jobs_.end());
374 Handle<SharedFunctionInfo>
function;
375 if (job->function.ToHandle(&
function)) {
376 GlobalHandles::Destroy(
function.location());
380 return jobs_.erase(it);