OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler-dispatcher/compiler-dispatcher.h" | 5 #include "src/compiler-dispatcher/compiler-dispatcher.h" |
6 | 6 |
7 #include "include/v8-platform.h" | 7 #include "include/v8-platform.h" |
8 #include "include/v8.h" | 8 #include "include/v8.h" |
9 #include "src/base/platform/time.h" | 9 #include "src/base/platform/time.h" |
10 #include "src/cancelable-task.h" | 10 #include "src/cancelable-task.h" |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 try_catch.ReThrow(); | 59 try_catch.ReThrow(); |
60 } | 60 } |
61 return job->status() != CompileJobStatus::kFailed; | 61 return job->status() != CompileJobStatus::kFailed; |
62 } | 62 } |
63 | 63 |
64 bool IsFinished(CompilerDispatcherJob* job) { | 64 bool IsFinished(CompilerDispatcherJob* job) { |
65 return job->status() == CompileJobStatus::kDone || | 65 return job->status() == CompileJobStatus::kDone || |
66 job->status() == CompileJobStatus::kFailed; | 66 job->status() == CompileJobStatus::kFailed; |
67 } | 67 } |
68 | 68 |
69 bool CanRunOnAnyThread(CompilerDispatcherJob* job) { | |
70 return (job->status() == CompileJobStatus::kReadyToParse && | |
71 job->can_parse_on_background_thread()) || | |
72 (job->status() == CompileJobStatus::kReadyToCompile && | |
73 job->can_compile_on_background_thread()); | |
74 } | |
75 | |
76 void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) { | |
77 DCHECK(CanRunOnAnyThread(job)); | |
78 switch (job->status()) { | |
79 case CompileJobStatus::kReadyToParse: | |
80 job->Parse(); | |
81 break; | |
82 | |
83 case CompileJobStatus::kReadyToCompile: | |
84 job->Compile(); | |
85 break; | |
86 | |
87 default: | |
88 UNREACHABLE(); | |
89 } | |
90 } | |
91 | |
92 // Theoretically we get 50ms of idle time max, however it's unlikely that | 69 // Theoretically we get 50ms of idle time max, however it's unlikely that |
93 // we'll get all of it so try to be a conservative. | 70 // we'll get all of it so try to be a conservative. |
94 const double kMaxIdleTimeToExpectInMs = 40; | 71 const double kMaxIdleTimeToExpectInMs = 40; |
95 | 72 |
96 } // namespace | 73 } // namespace |
97 | 74 |
98 class CompilerDispatcher::BackgroundTask : public CancelableTask { | |
99 public: | |
100 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager, | |
101 CompilerDispatcher* dispatcher); | |
102 ~BackgroundTask() override; | |
103 | |
104 // CancelableTask implementation. | |
105 void RunInternal() override; | |
106 | |
107 private: | |
108 CompilerDispatcher* dispatcher_; | |
109 | |
110 DISALLOW_COPY_AND_ASSIGN(BackgroundTask); | |
111 }; | |
112 | |
113 CompilerDispatcher::BackgroundTask::BackgroundTask( | |
114 Isolate* isolate, CancelableTaskManager* task_manager, | |
115 CompilerDispatcher* dispatcher) | |
116 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {} | |
117 | |
118 CompilerDispatcher::BackgroundTask::~BackgroundTask() {} | |
119 | |
120 void CompilerDispatcher::BackgroundTask::RunInternal() { | |
121 dispatcher_->DoBackgroundWork(); | |
122 } | |
123 | |
124 class CompilerDispatcher::IdleTask : public CancelableIdleTask { | 75 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
125 public: | 76 public: |
126 IdleTask(Isolate* isolate, CancelableTaskManager* task_manager, | 77 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); |
127 CompilerDispatcher* dispatcher); | |
128 ~IdleTask() override; | 78 ~IdleTask() override; |
129 | 79 |
130 // CancelableIdleTask implementation. | 80 // CancelableIdleTask implementation. |
131 void RunInternal(double deadline_in_seconds) override; | 81 void RunInternal(double deadline_in_seconds) override; |
132 | 82 |
133 private: | 83 private: |
134 CompilerDispatcher* dispatcher_; | 84 CompilerDispatcher* dispatcher_; |
135 | 85 |
136 DISALLOW_COPY_AND_ASSIGN(IdleTask); | 86 DISALLOW_COPY_AND_ASSIGN(IdleTask); |
137 }; | 87 }; |
138 | 88 |
139 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, | 89 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, |
140 CancelableTaskManager* task_manager, | |
141 CompilerDispatcher* dispatcher) | 90 CompilerDispatcher* dispatcher) |
142 : CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {} | 91 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} |
143 | 92 |
144 CompilerDispatcher::IdleTask::~IdleTask() {} | 93 CompilerDispatcher::IdleTask::~IdleTask() {} |
145 | 94 |
146 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | 95 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { |
147 dispatcher_->DoIdleWork(deadline_in_seconds); | 96 dispatcher_->DoIdleWork(deadline_in_seconds); |
148 } | 97 } |
149 | 98 |
150 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 99 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
151 size_t max_stack_size) | 100 size_t max_stack_size) |
152 : isolate_(isolate), | 101 : isolate_(isolate), |
153 platform_(platform), | 102 platform_(platform), |
154 max_stack_size_(max_stack_size), | 103 max_stack_size_(max_stack_size), |
155 tracer_(new CompilerDispatcherTracer(isolate_)), | 104 tracer_(new CompilerDispatcherTracer(isolate_)), |
156 task_manager_(new CancelableTaskManager()), | 105 idle_task_scheduled_(false) {} |
157 idle_task_scheduled_(false), | |
158 num_scheduled_background_tasks_(0), | |
159 main_thread_blocking_on_job_(nullptr) {} | |
160 | 106 |
161 CompilerDispatcher::~CompilerDispatcher() { | 107 CompilerDispatcher::~CompilerDispatcher() { |
162 // To avoid crashing in unit tests due to unfished jobs. | 108 // To avoid crashing in unit tests due to unfished jobs. |
163 AbortAll(BlockingBehavior::kBlock); | 109 AbortAll(BlockingBehavior::kBlock); |
164 task_manager_->CancelAndWait(); | |
165 } | 110 } |
166 | 111 |
167 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 112 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
168 if (!IsEnabled()) return false; | 113 if (!IsEnabled()) return false; |
169 | 114 |
170 // We only handle functions (no eval / top-level code / wasm) that are | 115 // We only handle functions (no eval / top-level code / wasm) that are |
171 // attached to a script. | 116 // attached to a script. |
172 if (!function->script()->IsScript() || !function->is_function() || | 117 if (!function->script()->IsScript() || !function->is_function() || |
173 function->asm_function() || function->native()) { | 118 function->asm_function() || function->native()) { |
174 return false; | 119 return false; |
(...skipping 11 matching lines...) Expand all Loading... |
186 | 131 |
187 bool CompilerDispatcher::IsEnabled() const { | 132 bool CompilerDispatcher::IsEnabled() const { |
188 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 133 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
189 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); | 134 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); |
190 } | 135 } |
191 | 136 |
192 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 137 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
193 return GetJobFor(function) != jobs_.end(); | 138 return GetJobFor(function) != jobs_.end(); |
194 } | 139 } |
195 | 140 |
196 void CompilerDispatcher::WaitForJobIfRunningOnBackground( | |
197 CompilerDispatcherJob* job) { | |
198 base::LockGuard<base::Mutex> lock(&mutex_); | |
199 if (running_background_jobs_.find(job) == running_background_jobs_.end()) { | |
200 pending_background_jobs_.erase(job); | |
201 return; | |
202 } | |
203 DCHECK_NULL(main_thread_blocking_on_job_); | |
204 main_thread_blocking_on_job_ = job; | |
205 while (main_thread_blocking_on_job_ != nullptr) { | |
206 main_thread_blocking_signal_.Wait(&mutex_); | |
207 } | |
208 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end()); | |
209 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end()); | |
210 } | |
211 | |
212 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 141 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
213 JobMap::const_iterator job = GetJobFor(function); | 142 JobMap::const_iterator job = GetJobFor(function); |
214 CHECK(job != jobs_.end()); | 143 CHECK(job != jobs_.end()); |
215 | 144 |
216 WaitForJobIfRunningOnBackground(job->second.get()); | 145 // TODO(jochen): Check if there's an in-flight background task working on this |
| 146 // job. |
217 while (!IsFinished(job->second.get())) { | 147 while (!IsFinished(job->second.get())) { |
218 DoNextStepOnMainThread(isolate_, job->second.get(), | 148 DoNextStepOnMainThread(isolate_, job->second.get(), |
219 ExceptionHandling::kThrow); | 149 ExceptionHandling::kThrow); |
220 } | 150 } |
221 bool result = job->second->status() != CompileJobStatus::kFailed; | 151 bool result = job->second->status() != CompileJobStatus::kFailed; |
222 job->second->ResetOnMainThread(); | 152 job->second->ResetOnMainThread(); |
223 jobs_.erase(job); | 153 jobs_.erase(job); |
224 return result; | 154 return result; |
225 } | 155 } |
226 | 156 |
| 157 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, |
| 158 BlockingBehavior blocking) { |
| 159 USE(blocking); |
| 160 JobMap::const_iterator job = GetJobFor(function); |
| 161 CHECK(job != jobs_.end()); |
| 162 |
| 163 // TODO(jochen): Check if there's an in-flight background task working on this |
| 164 // job. |
| 165 job->second->ResetOnMainThread(); |
| 166 jobs_.erase(job); |
| 167 } |
| 168 |
227 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 169 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
228 // TODO(jochen): Implement support for non-blocking abort. | 170 USE(blocking); |
229 DCHECK(blocking == BlockingBehavior::kBlock); | 171 // TODO(jochen): Check if there's an in-flight background task working on this |
| 172 // job. |
230 for (auto& kv : jobs_) { | 173 for (auto& kv : jobs_) { |
231 WaitForJobIfRunningOnBackground(kv.second.get()); | |
232 kv.second->ResetOnMainThread(); | 174 kv.second->ResetOnMainThread(); |
233 } | 175 } |
234 jobs_.clear(); | 176 jobs_.clear(); |
235 } | 177 } |
236 | 178 |
237 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 179 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
238 Handle<SharedFunctionInfo> shared) const { | 180 Handle<SharedFunctionInfo> shared) const { |
239 if (!shared->script()->IsScript()) return jobs_.end(); | 181 if (!shared->script()->IsScript()) return jobs_.end(); |
240 std::pair<int, int> key(Script::cast(shared->script())->id(), | 182 std::pair<int, int> key(Script::cast(shared->script())->id(), |
241 shared->function_literal_id()); | 183 shared->function_literal_id()); |
242 auto range = jobs_.equal_range(key); | 184 auto range = jobs_.equal_range(key); |
243 for (auto job = range.first; job != range.second; ++job) { | 185 for (auto job = range.first; job != range.second; ++job) { |
244 if (job->second->IsAssociatedWith(shared)) return job; | 186 if (job->second->IsAssociatedWith(shared)) return job; |
245 } | 187 } |
246 return jobs_.end(); | 188 return jobs_.end(); |
247 } | 189 } |
248 | 190 |
249 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() { | 191 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
250 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 192 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
251 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); | 193 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); |
252 { | 194 if (idle_task_scheduled_) return; |
253 base::LockGuard<base::Mutex> lock(&mutex_); | |
254 if (idle_task_scheduled_) return; | |
255 idle_task_scheduled_ = true; | |
256 } | |
257 platform_->CallIdleOnForegroundThread( | |
258 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); | |
259 } | |
260 | |
261 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | |
262 if (jobs_.empty()) return; | 195 if (jobs_.empty()) return; |
263 ScheduleIdleTaskFromAnyThread(); | 196 idle_task_scheduled_ = true; |
264 } | 197 platform_->CallIdleOnForegroundThread(v8_isolate, |
265 | 198 new IdleTask(isolate_, this)); |
266 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( | |
267 CompilerDispatcherJob* job) { | |
268 if (!CanRunOnAnyThread(job)) return; | |
269 { | |
270 base::LockGuard<base::Mutex> lock(&mutex_); | |
271 pending_background_jobs_.insert(job); | |
272 } | |
273 ScheduleMoreBackgroundTasksIfNeeded(); | |
274 } | |
275 | |
276 void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { | |
277 if (FLAG_single_threaded) return; | |
278 { | |
279 base::LockGuard<base::Mutex> lock(&mutex_); | |
280 if (pending_background_jobs_.empty()) return; | |
281 if (platform_->NumberOfAvailableBackgroundThreads() <= | |
282 num_scheduled_background_tasks_) { | |
283 return; | |
284 } | |
285 ++num_scheduled_background_tasks_; | |
286 } | |
287 platform_->CallOnBackgroundThread( | |
288 new BackgroundTask(isolate_, task_manager_.get(), this), | |
289 v8::Platform::kShortRunningTask); | |
290 } | |
291 | |
292 void CompilerDispatcher::DoBackgroundWork() { | |
293 CompilerDispatcherJob* job = nullptr; | |
294 { | |
295 base::LockGuard<base::Mutex> lock(&mutex_); | |
296 --num_scheduled_background_tasks_; | |
297 if (!pending_background_jobs_.empty()) { | |
298 auto it = pending_background_jobs_.begin(); | |
299 job = *it; | |
300 pending_background_jobs_.erase(it); | |
301 running_background_jobs_.insert(job); | |
302 } | |
303 } | |
304 if (job == nullptr) return; | |
305 DoNextStepOnBackgroundThread(job); | |
306 | |
307 ScheduleMoreBackgroundTasksIfNeeded(); | |
308 // Unconditionally schedule an idle task, as all background steps have to be | |
309 // followed by a main thread step. | |
310 ScheduleIdleTaskFromAnyThread(); | |
311 | |
312 { | |
313 base::LockGuard<base::Mutex> lock(&mutex_); | |
314 running_background_jobs_.erase(job); | |
315 | |
316 if (main_thread_blocking_on_job_ == job) { | |
317 main_thread_blocking_on_job_ = nullptr; | |
318 main_thread_blocking_signal_.NotifyOne(); | |
319 } | |
320 } | |
321 // Don't touch |this| anymore after this point, as it might have been | |
322 // deleted. | |
323 } | 199 } |
324 | 200 |
325 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | 201 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
326 { | 202 idle_task_scheduled_ = false; |
327 base::LockGuard<base::Mutex> lock(&mutex_); | |
328 idle_task_scheduled_ = false; | |
329 } | |
330 | 203 |
331 // Number of jobs that are unlikely to make progress during any idle callback | 204 // Number of jobs that are unlikely to make progress during any idle callback |
332 // due to their estimated duration. | 205 // due to their estimated duration. |
333 size_t too_long_jobs = 0; | 206 size_t too_long_jobs = 0; |
334 | 207 |
335 // Iterate over all available jobs & remaining time. For each job, decide | 208 // Iterate over all available jobs & remaining time. For each job, decide |
336 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | 209 // whether to 1) skip it (if it would take too long), 2) erase it (if it's |
337 // finished), or 3) make progress on it. | 210 // finished), or 3) make progress on it. |
338 double idle_time_in_seconds = | 211 double idle_time_in_seconds = |
339 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | 212 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
340 for (auto job = jobs_.begin(); | 213 for (auto job = jobs_.begin(); |
341 job != jobs_.end() && idle_time_in_seconds > 0.0; | 214 job != jobs_.end() && idle_time_in_seconds > 0.0; |
342 idle_time_in_seconds = | 215 idle_time_in_seconds = |
343 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | 216 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { |
344 // Don't work on jobs that are being worked on by background tasks. | |
345 // Similarly, remove jobs we work on from the set of available background | |
346 // jobs. | |
347 std::unique_ptr<base::LockGuard<base::Mutex>> lock( | |
348 new base::LockGuard<base::Mutex>(&mutex_)); | |
349 if (running_background_jobs_.find(job->second.get()) != | |
350 running_background_jobs_.end()) { | |
351 ++job; | |
352 continue; | |
353 } | |
354 auto it = pending_background_jobs_.find(job->second.get()); | |
355 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); | 217 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); |
356 if (idle_time_in_seconds < | 218 if (idle_time_in_seconds < |
357 (estimate_in_ms / | 219 (estimate_in_ms / |
358 static_cast<double>(base::Time::kMillisecondsPerSecond))) { | 220 static_cast<double>(base::Time::kMillisecondsPerSecond))) { |
359 // If there's not enough time left, try to estimate whether we would | 221 // If there's not enough time left, try to estimate whether we would |
360 // have managed to finish the job in a large idle task to assess | 222 // have managed to finish the job in a large idle task to assess |
361 // whether we should ask for another idle callback. | 223 // whether we should ask for another idle callback. |
362 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; | 224 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; |
363 if (it == pending_background_jobs_.end()) { | |
364 lock.reset(); | |
365 ConsiderJobForBackgroundProcessing(job->second.get()); | |
366 } | |
367 ++job; | 225 ++job; |
368 } else if (IsFinished(job->second.get())) { | 226 } else if (IsFinished(job->second.get())) { |
369 DCHECK(it == pending_background_jobs_.end()); | |
370 job->second->ResetOnMainThread(); | 227 job->second->ResetOnMainThread(); |
371 job = jobs_.erase(job); | 228 job = jobs_.erase(job); |
372 continue; | 229 break; |
373 } else { | 230 } else { |
374 // Do one step, and keep processing the job (as we don't advance the | 231 // Do one step, and keep processing the job (as we don't advance the |
375 // iterator). | 232 // iterator). |
376 if (it != pending_background_jobs_.end()) { | |
377 pending_background_jobs_.erase(it); | |
378 } | |
379 lock.reset(); | |
380 DoNextStepOnMainThread(isolate_, job->second.get(), | 233 DoNextStepOnMainThread(isolate_, job->second.get(), |
381 ExceptionHandling::kSwallow); | 234 ExceptionHandling::kSwallow); |
382 } | 235 } |
383 } | 236 } |
384 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 237 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
385 } | 238 } |
386 | 239 |
387 } // namespace internal | 240 } // namespace internal |
388 } // namespace v8 | 241 } // namespace v8 |
OLD | NEW |