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