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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
45 | 45 |
46 case CompileJobStatus::kCompiled: | 46 case CompileJobStatus::kCompiled: |
47 job->FinalizeCompilingOnMainThread(); | 47 job->FinalizeCompilingOnMainThread(); |
48 break; | 48 break; |
49 | 49 |
50 case CompileJobStatus::kFailed: | 50 case CompileJobStatus::kFailed: |
51 case CompileJobStatus::kDone: | 51 case CompileJobStatus::kDone: |
52 break; | 52 break; |
53 } | 53 } |
54 | 54 |
55 if (job->status() == CompileJobStatus::kFailed) { | 55 DCHECK_EQ(job->status() == CompileJobStatus::kFailed, |
56 DCHECK(isolate->has_pending_exception()); | 56 isolate->has_pending_exception()); |
57 if (exception_handling == ExceptionHandling::kSwallow) { | 57 if (job->status() == CompileJobStatus::kFailed && |
58 isolate->clear_pending_exception(); | 58 exception_handling == ExceptionHandling::kSwallow) { |
59 } | 59 isolate->clear_pending_exception(); |
60 } else { | |
61 DCHECK(!isolate->has_pending_exception()); | |
62 } | 60 } |
63 return job->status() != CompileJobStatus::kFailed; | 61 return job->status() != CompileJobStatus::kFailed; |
64 } | 62 } |
65 | 63 |
66 bool IsFinished(CompilerDispatcherJob* job) { | 64 bool IsFinished(CompilerDispatcherJob* job) { |
67 return job->status() == CompileJobStatus::kDone || | 65 return job->status() == CompileJobStatus::kDone || |
68 job->status() == CompileJobStatus::kFailed; | 66 job->status() == CompileJobStatus::kFailed; |
69 } | 67 } |
70 | 68 |
71 bool CanRunOnAnyThread(CompilerDispatcherJob* job) { | 69 bool CanRunOnAnyThread(CompilerDispatcherJob* job) { |
(...skipping 18 matching lines...) Expand all Loading... | |
90 UNREACHABLE(); | 88 UNREACHABLE(); |
91 } | 89 } |
92 } | 90 } |
93 | 91 |
94 // Theoretically we get 50ms of idle time max, however it's unlikely that | 92 // Theoretically we get 50ms of idle time max, however it's unlikely that |
95 // we'll get all of it so try to be a conservative. | 93 // we'll get all of it so try to be a conservative. |
96 const double kMaxIdleTimeToExpectInMs = 40; | 94 const double kMaxIdleTimeToExpectInMs = 40; |
97 | 95 |
98 } // namespace | 96 } // namespace |
99 | 97 |
98 class CompilerDispatcher::AbortTask : public CancelableTask { | |
99 public: | |
100 AbortTask(Isolate* isolate, CancelableTaskManager* task_manager, | |
101 CompilerDispatcher* dispatcher); | |
102 ~AbortTask() override; | |
103 | |
104 // CancelableTask implementation. | |
105 void RunInternal() override; | |
106 | |
107 private: | |
108 CompilerDispatcher* dispatcher_; | |
109 | |
110 DISALLOW_COPY_AND_ASSIGN(AbortTask); | |
111 }; | |
112 | |
113 CompilerDispatcher::AbortTask::AbortTask(Isolate* isolate, | |
114 CancelableTaskManager* task_manager, | |
115 CompilerDispatcher* dispatcher) | |
116 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {} | |
117 | |
118 CompilerDispatcher::AbortTask::~AbortTask() {} | |
119 | |
120 void CompilerDispatcher::AbortTask::RunInternal() { | |
121 dispatcher_->AbortInactiveJobs(); | |
122 } | |
123 | |
100 class CompilerDispatcher::BackgroundTask : public CancelableTask { | 124 class CompilerDispatcher::BackgroundTask : public CancelableTask { |
101 public: | 125 public: |
102 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager, | 126 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager, |
103 CompilerDispatcher* dispatcher); | 127 CompilerDispatcher* dispatcher); |
104 ~BackgroundTask() override; | 128 ~BackgroundTask() override; |
105 | 129 |
106 // CancelableTask implementation. | 130 // CancelableTask implementation. |
107 void RunInternal() override; | 131 void RunInternal() override; |
108 | 132 |
109 private: | 133 private: |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
149 dispatcher_->DoIdleWork(deadline_in_seconds); | 173 dispatcher_->DoIdleWork(deadline_in_seconds); |
150 } | 174 } |
151 | 175 |
152 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 176 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
153 size_t max_stack_size) | 177 size_t max_stack_size) |
154 : isolate_(isolate), | 178 : isolate_(isolate), |
155 platform_(platform), | 179 platform_(platform), |
156 max_stack_size_(max_stack_size), | 180 max_stack_size_(max_stack_size), |
157 tracer_(new CompilerDispatcherTracer(isolate_)), | 181 tracer_(new CompilerDispatcherTracer(isolate_)), |
158 task_manager_(new CancelableTaskManager()), | 182 task_manager_(new CancelableTaskManager()), |
183 abort_(false), | |
159 idle_task_scheduled_(false), | 184 idle_task_scheduled_(false), |
160 num_scheduled_background_tasks_(0), | 185 num_scheduled_background_tasks_(0), |
161 main_thread_blocking_on_job_(nullptr) {} | 186 main_thread_blocking_on_job_(nullptr), |
187 block_for_testing_(false), | |
188 semaphore_for_testing_(0) {} | |
162 | 189 |
163 CompilerDispatcher::~CompilerDispatcher() { | 190 CompilerDispatcher::~CompilerDispatcher() { |
164 // To avoid crashing in unit tests due to unfished jobs. | 191 // To avoid crashing in unit tests due to unfished jobs. |
165 AbortAll(BlockingBehavior::kBlock); | 192 AbortAll(BlockingBehavior::kBlock); |
166 task_manager_->CancelAndWait(); | 193 task_manager_->CancelAndWait(); |
167 } | 194 } |
168 | 195 |
169 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 196 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
170 if (!IsEnabled()) return false; | 197 if (!IsEnabled()) return false; |
171 | 198 |
199 { | |
200 base::LockGuard<base::Mutex> lock(&mutex_); | |
201 if (abort_) return false; | |
202 } | |
203 | |
172 // We only handle functions (no eval / top-level code / wasm) that are | 204 // We only handle functions (no eval / top-level code / wasm) that are |
173 // attached to a script. | 205 // attached to a script. |
174 if (!function->script()->IsScript() || !function->is_function() || | 206 if (!function->script()->IsScript() || !function->is_function() || |
175 function->asm_function() || function->native()) { | 207 function->asm_function() || function->native()) { |
176 return false; | 208 return false; |
177 } | 209 } |
178 | 210 |
179 if (IsEnqueued(function)) return true; | 211 if (IsEnqueued(function)) return true; |
180 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( | 212 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( |
181 isolate_, tracer_.get(), function, max_stack_size_)); | 213 isolate_, tracer_.get(), function, max_stack_size_)); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
216 CHECK(job != jobs_.end()); | 248 CHECK(job != jobs_.end()); |
217 | 249 |
218 WaitForJobIfRunningOnBackground(job->second.get()); | 250 WaitForJobIfRunningOnBackground(job->second.get()); |
219 while (!IsFinished(job->second.get())) { | 251 while (!IsFinished(job->second.get())) { |
220 DoNextStepOnMainThread(isolate_, job->second.get(), | 252 DoNextStepOnMainThread(isolate_, job->second.get(), |
221 ExceptionHandling::kThrow); | 253 ExceptionHandling::kThrow); |
222 } | 254 } |
223 bool result = job->second->status() != CompileJobStatus::kFailed; | 255 bool result = job->second->status() != CompileJobStatus::kFailed; |
224 job->second->ResetOnMainThread(); | 256 job->second->ResetOnMainThread(); |
225 jobs_.erase(job); | 257 jobs_.erase(job); |
258 if (jobs_.empty()) { | |
259 base::LockGuard<base::Mutex> lock(&mutex_); | |
260 abort_ = false; | |
261 } | |
226 return result; | 262 return result; |
227 } | 263 } |
228 | 264 |
229 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 265 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
230 // TODO(jochen): Implement support for non-blocking abort. | 266 bool background_tasks_running = |
231 DCHECK(blocking == BlockingBehavior::kBlock); | 267 task_manager_->Cancel() == CancelableTaskManager::kTaskRunning; |
232 for (auto& kv : jobs_) { | 268 if (!background_tasks_running || blocking == BlockingBehavior::kBlock) { |
233 WaitForJobIfRunningOnBackground(kv.second.get()); | 269 for (auto& kv : jobs_) { |
vogelheim
2017/01/04 12:58:08
kv ???
jochen (gone - plz use gerrit)
2017/01/04 13:18:31
key/value... guess "it" is the more common name
| |
234 kv.second->ResetOnMainThread(); | 270 WaitForJobIfRunningOnBackground(kv.second.get()); |
vogelheim
2017/01/04 12:58:08
If !background_tasks_running, we expect WaitForJob
jochen (gone - plz use gerrit)
2017/01/04 13:18:31
right
| |
271 kv.second->ResetOnMainThread(); | |
272 } | |
273 jobs_.clear(); | |
274 { | |
275 base::LockGuard<base::Mutex> lock(&mutex_); | |
276 DCHECK(pending_background_jobs_.empty()); | |
277 DCHECK(running_background_jobs_.empty()); | |
278 abort_ = false; | |
279 } | |
280 return; | |
235 } | 281 } |
236 jobs_.clear(); | 282 |
283 { | |
284 base::LockGuard<base::Mutex> lock(&mutex_); | |
285 abort_ = true; | |
286 pending_background_jobs_.clear(); | |
287 } | |
288 AbortInactiveJobs(); | |
289 | |
290 // All running background jobs might already have scheduled idle tasks instead | |
291 // of abort tasks. Schedule a single abort task here to make sure they get | |
292 // processed as soon as possible (and not first when we have idle time). | |
293 ScheduleAbortTask(); | |
294 } | |
295 | |
296 void CompilerDispatcher::AbortInactiveJobs() { | |
297 { | |
298 base::LockGuard<base::Mutex> lock(&mutex_); | |
299 // Since we schedule two abort tasks per async abort, we might end up | |
300 // here with nothing left to do. | |
301 if (!abort_) return; | |
302 } | |
303 for (auto it = jobs_.begin(); it != jobs_.end();) { | |
304 auto job = it; | |
305 ++it; | |
306 { | |
307 base::LockGuard<base::Mutex> lock(&mutex_); | |
308 if (running_background_jobs_.find(job->second.get()) != | |
309 running_background_jobs_.end()) { | |
310 continue; | |
311 } | |
312 } | |
313 job->second->ResetOnMainThread(); | |
314 jobs_.erase(job); | |
315 } | |
316 if (jobs_.empty()) { | |
317 base::LockGuard<base::Mutex> lock(&mutex_); | |
318 abort_ = false; | |
319 } | |
237 } | 320 } |
238 | 321 |
239 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 322 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
240 Handle<SharedFunctionInfo> shared) const { | 323 Handle<SharedFunctionInfo> shared) const { |
241 if (!shared->script()->IsScript()) return jobs_.end(); | 324 if (!shared->script()->IsScript()) return jobs_.end(); |
242 std::pair<int, int> key(Script::cast(shared->script())->id(), | 325 std::pair<int, int> key(Script::cast(shared->script())->id(), |
243 shared->function_literal_id()); | 326 shared->function_literal_id()); |
244 auto range = jobs_.equal_range(key); | 327 auto range = jobs_.equal_range(key); |
245 for (auto job = range.first; job != range.second; ++job) { | 328 for (auto job = range.first; job != range.second; ++job) { |
246 if (job->second->IsAssociatedWith(shared)) return job; | 329 if (job->second->IsAssociatedWith(shared)) return job; |
(...skipping 11 matching lines...) Expand all Loading... | |
258 } | 341 } |
259 platform_->CallIdleOnForegroundThread( | 342 platform_->CallIdleOnForegroundThread( |
260 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); | 343 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); |
261 } | 344 } |
262 | 345 |
263 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | 346 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
264 if (jobs_.empty()) return; | 347 if (jobs_.empty()) return; |
265 ScheduleIdleTaskFromAnyThread(); | 348 ScheduleIdleTaskFromAnyThread(); |
266 } | 349 } |
267 | 350 |
351 void CompilerDispatcher::ScheduleAbortTask() { | |
352 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |
353 platform_->CallOnForegroundThread( | |
354 v8_isolate, new AbortTask(isolate_, task_manager_.get(), this)); | |
355 } | |
356 | |
268 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( | 357 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( |
269 CompilerDispatcherJob* job) { | 358 CompilerDispatcherJob* job) { |
270 if (!CanRunOnAnyThread(job)) return; | 359 if (!CanRunOnAnyThread(job)) return; |
271 { | 360 { |
272 base::LockGuard<base::Mutex> lock(&mutex_); | 361 base::LockGuard<base::Mutex> lock(&mutex_); |
273 pending_background_jobs_.insert(job); | 362 pending_background_jobs_.insert(job); |
274 } | 363 } |
275 ScheduleMoreBackgroundTasksIfNeeded(); | 364 ScheduleMoreBackgroundTasksIfNeeded(); |
276 } | 365 } |
277 | 366 |
(...skipping 19 matching lines...) Expand all Loading... | |
297 base::LockGuard<base::Mutex> lock(&mutex_); | 386 base::LockGuard<base::Mutex> lock(&mutex_); |
298 --num_scheduled_background_tasks_; | 387 --num_scheduled_background_tasks_; |
299 if (!pending_background_jobs_.empty()) { | 388 if (!pending_background_jobs_.empty()) { |
300 auto it = pending_background_jobs_.begin(); | 389 auto it = pending_background_jobs_.begin(); |
301 job = *it; | 390 job = *it; |
302 pending_background_jobs_.erase(it); | 391 pending_background_jobs_.erase(it); |
303 running_background_jobs_.insert(job); | 392 running_background_jobs_.insert(job); |
304 } | 393 } |
305 } | 394 } |
306 if (job == nullptr) return; | 395 if (job == nullptr) return; |
396 | |
397 if (V8_UNLIKELY(block_for_testing_.Value())) { | |
398 block_for_testing_.SetValue(false); | |
399 semaphore_for_testing_.Wait(); | |
400 } | |
401 | |
307 DoNextStepOnBackgroundThread(job); | 402 DoNextStepOnBackgroundThread(job); |
308 | 403 |
309 ScheduleMoreBackgroundTasksIfNeeded(); | 404 ScheduleMoreBackgroundTasksIfNeeded(); |
310 // Unconditionally schedule an idle task, as all background steps have to be | 405 // Unconditionally schedule an idle task, as all background steps have to be |
311 // followed by a main thread step. | 406 // followed by a main thread step. |
312 ScheduleIdleTaskFromAnyThread(); | 407 ScheduleIdleTaskFromAnyThread(); |
313 | 408 |
314 { | 409 { |
315 base::LockGuard<base::Mutex> lock(&mutex_); | 410 base::LockGuard<base::Mutex> lock(&mutex_); |
316 running_background_jobs_.erase(job); | 411 running_background_jobs_.erase(job); |
317 | 412 |
413 if (running_background_jobs_.empty() && abort_) { | |
414 // This is the last background job that finished. The abort task | |
415 // scheduled by AbortAll might already have ran, so schedule another | |
416 // one to be on the safe side. | |
417 ScheduleAbortTask(); | |
418 } | |
419 | |
318 if (main_thread_blocking_on_job_ == job) { | 420 if (main_thread_blocking_on_job_ == job) { |
319 main_thread_blocking_on_job_ = nullptr; | 421 main_thread_blocking_on_job_ = nullptr; |
320 main_thread_blocking_signal_.NotifyOne(); | 422 main_thread_blocking_signal_.NotifyOne(); |
321 } | 423 } |
322 } | 424 } |
323 // Don't touch |this| anymore after this point, as it might have been | 425 // Don't touch |this| anymore after this point, as it might have been |
324 // deleted. | 426 // deleted. |
325 } | 427 } |
326 | 428 |
327 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | 429 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
430 bool aborted = false; | |
328 { | 431 { |
329 base::LockGuard<base::Mutex> lock(&mutex_); | 432 base::LockGuard<base::Mutex> lock(&mutex_); |
330 idle_task_scheduled_ = false; | 433 idle_task_scheduled_ = false; |
434 aborted = abort_; | |
435 } | |
436 | |
437 if (aborted) { | |
438 AbortInactiveJobs(); | |
439 return; | |
331 } | 440 } |
332 | 441 |
333 // Number of jobs that are unlikely to make progress during any idle callback | 442 // Number of jobs that are unlikely to make progress during any idle callback |
334 // due to their estimated duration. | 443 // due to their estimated duration. |
335 size_t too_long_jobs = 0; | 444 size_t too_long_jobs = 0; |
336 | 445 |
337 // Iterate over all available jobs & remaining time. For each job, decide | 446 // Iterate over all available jobs & remaining time. For each job, decide |
338 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | 447 // whether to 1) skip it (if it would take too long), 2) erase it (if it's |
339 // finished), or 3) make progress on it. | 448 // finished), or 3) make progress on it. |
340 double idle_time_in_seconds = | 449 double idle_time_in_seconds = |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
381 lock.reset(); | 490 lock.reset(); |
382 DoNextStepOnMainThread(isolate_, job->second.get(), | 491 DoNextStepOnMainThread(isolate_, job->second.get(), |
383 ExceptionHandling::kSwallow); | 492 ExceptionHandling::kSwallow); |
384 } | 493 } |
385 } | 494 } |
386 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 495 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
387 } | 496 } |
388 | 497 |
389 } // namespace internal | 498 } // namespace internal |
390 } // namespace v8 | 499 } // namespace v8 |
OLD | NEW |