| 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" |
| 11 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" | 11 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" |
| 12 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" | 12 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" |
| 13 #include "src/flags.h" |
| 13 #include "src/objects-inl.h" | 14 #include "src/objects-inl.h" |
| 14 | 15 |
| 15 namespace v8 { | 16 namespace v8 { |
| 16 namespace internal { | 17 namespace internal { |
| 17 | 18 |
| 18 namespace { | 19 namespace { |
| 19 | 20 |
| 20 enum class ExceptionHandling { kSwallow, kThrow }; | 21 enum class ExceptionHandling { kSwallow, kThrow }; |
| 21 | 22 |
| 22 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job, | 23 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job, |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 | 198 |
| 198 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | 199 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { |
| 199 dispatcher_->DoIdleWork(deadline_in_seconds); | 200 dispatcher_->DoIdleWork(deadline_in_seconds); |
| 200 } | 201 } |
| 201 | 202 |
| 202 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 203 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
| 203 size_t max_stack_size) | 204 size_t max_stack_size) |
| 204 : isolate_(isolate), | 205 : isolate_(isolate), |
| 205 platform_(platform), | 206 platform_(platform), |
| 206 max_stack_size_(max_stack_size), | 207 max_stack_size_(max_stack_size), |
| 208 trace_compiler_dispatcher_(FLAG_trace_compiler_dispatcher), |
| 207 tracer_(new CompilerDispatcherTracer(isolate_)), | 209 tracer_(new CompilerDispatcherTracer(isolate_)), |
| 208 task_manager_(new CancelableTaskManager()), | 210 task_manager_(new CancelableTaskManager()), |
| 209 memory_pressure_level_(MemoryPressureLevel::kNone), | 211 memory_pressure_level_(MemoryPressureLevel::kNone), |
| 210 abort_(false), | 212 abort_(false), |
| 211 idle_task_scheduled_(false), | 213 idle_task_scheduled_(false), |
| 212 num_scheduled_background_tasks_(0), | 214 num_scheduled_background_tasks_(0), |
| 213 main_thread_blocking_on_job_(nullptr), | 215 main_thread_blocking_on_job_(nullptr), |
| 214 block_for_testing_(false), | 216 block_for_testing_(false), |
| 215 semaphore_for_testing_(0) {} | 217 semaphore_for_testing_(0) { |
| 218 if (trace_compiler_dispatcher_ && !IsEnabled()) { |
| 219 PrintF("CompilerDispatcher: dispatcher is disabled\n"); |
| 220 } |
| 221 } |
| 216 | 222 |
| 217 CompilerDispatcher::~CompilerDispatcher() { | 223 CompilerDispatcher::~CompilerDispatcher() { |
| 218 // To avoid crashing in unit tests due to unfished jobs. | 224 // To avoid crashing in unit tests due to unfished jobs. |
| 219 AbortAll(BlockingBehavior::kBlock); | 225 AbortAll(BlockingBehavior::kBlock); |
| 220 task_manager_->CancelAndWait(); | 226 task_manager_->CancelAndWait(); |
| 221 } | 227 } |
| 222 | 228 |
| 223 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 229 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
| 224 if (!IsEnabled()) return false; | 230 if (!IsEnabled()) return false; |
| 225 | 231 |
| 226 if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) { | 232 if (memory_pressure_level_.Value() != MemoryPressureLevel::kNone) { |
| 227 return false; | 233 return false; |
| 228 } | 234 } |
| 229 | 235 |
| 230 { | 236 { |
| 231 base::LockGuard<base::Mutex> lock(&mutex_); | 237 base::LockGuard<base::Mutex> lock(&mutex_); |
| 232 if (abort_) return false; | 238 if (abort_) return false; |
| 233 } | 239 } |
| 234 | 240 |
| 235 // We only handle functions (no eval / top-level code / wasm) that are | 241 // We only handle functions (no eval / top-level code / wasm) that are |
| 236 // attached to a script. | 242 // attached to a script. |
| 237 if (!function->script()->IsScript() || !function->is_function() || | 243 if (!function->script()->IsScript() || !function->is_function() || |
| 238 function->asm_function() || function->native()) { | 244 function->asm_function() || function->native()) { |
| 239 return false; | 245 return false; |
| 240 } | 246 } |
| 241 | 247 |
| 242 if (IsEnqueued(function)) return true; | 248 if (IsEnqueued(function)) return true; |
| 249 |
| 250 if (trace_compiler_dispatcher_) { |
| 251 PrintF("CompilerDispatcher: enqueuing "); |
| 252 function->ShortPrint(); |
| 253 PrintF("\n"); |
| 254 } |
| 255 |
| 243 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( | 256 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( |
| 244 isolate_, tracer_.get(), function, max_stack_size_)); | 257 isolate_, tracer_.get(), function, max_stack_size_)); |
| 245 std::pair<int, int> key(Script::cast(function->script())->id(), | 258 std::pair<int, int> key(Script::cast(function->script())->id(), |
| 246 function->function_literal_id()); | 259 function->function_literal_id()); |
| 247 jobs_.insert(std::make_pair(key, std::move(job))); | 260 jobs_.insert(std::make_pair(key, std::move(job))); |
| 248 ScheduleIdleTaskIfNeeded(); | 261 ScheduleIdleTaskIfNeeded(); |
| 249 return true; | 262 return true; |
| 250 } | 263 } |
| 251 | 264 |
| 252 bool CompilerDispatcher::IsEnabled() const { | 265 bool CompilerDispatcher::IsEnabled() const { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 271 main_thread_blocking_signal_.Wait(&mutex_); | 284 main_thread_blocking_signal_.Wait(&mutex_); |
| 272 } | 285 } |
| 273 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end()); | 286 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end()); |
| 274 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end()); | 287 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end()); |
| 275 } | 288 } |
| 276 | 289 |
| 277 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 290 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
| 278 JobMap::const_iterator job = GetJobFor(function); | 291 JobMap::const_iterator job = GetJobFor(function); |
| 279 CHECK(job != jobs_.end()); | 292 CHECK(job != jobs_.end()); |
| 280 | 293 |
| 294 if (trace_compiler_dispatcher_) { |
| 295 PrintF("CompilerDispatcher: finishing "); |
| 296 function->ShortPrint(); |
| 297 PrintF(" now\n"); |
| 298 } |
| 299 |
| 281 WaitForJobIfRunningOnBackground(job->second.get()); | 300 WaitForJobIfRunningOnBackground(job->second.get()); |
| 282 while (!IsFinished(job->second.get())) { | 301 while (!IsFinished(job->second.get())) { |
| 283 DoNextStepOnMainThread(isolate_, job->second.get(), | 302 DoNextStepOnMainThread(isolate_, job->second.get(), |
| 284 ExceptionHandling::kThrow); | 303 ExceptionHandling::kThrow); |
| 285 } | 304 } |
| 286 bool result = job->second->status() != CompileJobStatus::kFailed; | 305 bool result = job->second->status() != CompileJobStatus::kFailed; |
| 306 |
| 307 if (trace_compiler_dispatcher_) { |
| 308 PrintF("CompilerDispatcher: finished working on "); |
| 309 function->ShortPrint(); |
| 310 PrintF(": %s\n", result ? "success" : "failure"); |
| 311 tracer_->DumpStatistics(); |
| 312 } |
| 313 |
| 287 job->second->ResetOnMainThread(); | 314 job->second->ResetOnMainThread(); |
| 288 jobs_.erase(job); | 315 jobs_.erase(job); |
| 289 if (jobs_.empty()) { | 316 if (jobs_.empty()) { |
| 290 base::LockGuard<base::Mutex> lock(&mutex_); | 317 base::LockGuard<base::Mutex> lock(&mutex_); |
| 291 abort_ = false; | 318 abort_ = false; |
| 292 } | 319 } |
| 293 return result; | 320 return result; |
| 294 } | 321 } |
| 295 | 322 |
| 296 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 323 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
| 297 bool background_tasks_running = | 324 bool background_tasks_running = |
| 298 task_manager_->TryAbortAll() == CancelableTaskManager::kTaskRunning; | 325 task_manager_->TryAbortAll() == CancelableTaskManager::kTaskRunning; |
| 299 if (!background_tasks_running || blocking == BlockingBehavior::kBlock) { | 326 if (!background_tasks_running || blocking == BlockingBehavior::kBlock) { |
| 300 for (auto& it : jobs_) { | 327 for (auto& it : jobs_) { |
| 301 WaitForJobIfRunningOnBackground(it.second.get()); | 328 WaitForJobIfRunningOnBackground(it.second.get()); |
| 329 if (trace_compiler_dispatcher_) { |
| 330 PrintF("CompilerDispatcher: aborted "); |
| 331 it.second->ShortPrint(); |
| 332 PrintF("\n"); |
| 333 } |
| 302 it.second->ResetOnMainThread(); | 334 it.second->ResetOnMainThread(); |
| 303 } | 335 } |
| 304 jobs_.clear(); | 336 jobs_.clear(); |
| 305 { | 337 { |
| 306 base::LockGuard<base::Mutex> lock(&mutex_); | 338 base::LockGuard<base::Mutex> lock(&mutex_); |
| 307 DCHECK(pending_background_jobs_.empty()); | 339 DCHECK(pending_background_jobs_.empty()); |
| 308 DCHECK(running_background_jobs_.empty()); | 340 DCHECK(running_background_jobs_.empty()); |
| 309 abort_ = false; | 341 abort_ = false; |
| 310 } | 342 } |
| 311 return; | 343 return; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 334 for (auto it = jobs_.begin(); it != jobs_.end();) { | 366 for (auto it = jobs_.begin(); it != jobs_.end();) { |
| 335 auto job = it; | 367 auto job = it; |
| 336 ++it; | 368 ++it; |
| 337 { | 369 { |
| 338 base::LockGuard<base::Mutex> lock(&mutex_); | 370 base::LockGuard<base::Mutex> lock(&mutex_); |
| 339 if (running_background_jobs_.find(job->second.get()) != | 371 if (running_background_jobs_.find(job->second.get()) != |
| 340 running_background_jobs_.end()) { | 372 running_background_jobs_.end()) { |
| 341 continue; | 373 continue; |
| 342 } | 374 } |
| 343 } | 375 } |
| 376 if (trace_compiler_dispatcher_) { |
| 377 PrintF("CompilerDispatcher: aborted "); |
| 378 job->second->ShortPrint(); |
| 379 PrintF("\n"); |
| 380 } |
| 344 job->second->ResetOnMainThread(); | 381 job->second->ResetOnMainThread(); |
| 345 jobs_.erase(job); | 382 jobs_.erase(job); |
| 346 } | 383 } |
| 347 if (jobs_.empty()) { | 384 if (jobs_.empty()) { |
| 348 base::LockGuard<base::Mutex> lock(&mutex_); | 385 base::LockGuard<base::Mutex> lock(&mutex_); |
| 349 abort_ = false; | 386 abort_ = false; |
| 350 } | 387 } |
| 351 } | 388 } |
| 352 | 389 |
| 353 void CompilerDispatcher::MemoryPressureNotification( | 390 void CompilerDispatcher::MemoryPressureNotification( |
| 354 v8::MemoryPressureLevel level, bool is_isolate_locked) { | 391 v8::MemoryPressureLevel level, bool is_isolate_locked) { |
| 355 MemoryPressureLevel previous = memory_pressure_level_.Value(); | 392 MemoryPressureLevel previous = memory_pressure_level_.Value(); |
| 356 memory_pressure_level_.SetValue(level); | 393 memory_pressure_level_.SetValue(level); |
| 357 // If we're already under pressure, we haven't accepted new tasks meanwhile | 394 // If we're already under pressure, we haven't accepted new tasks meanwhile |
| 358 // and can just return. If we're no longer under pressure, we're also done. | 395 // and can just return. If we're no longer under pressure, we're also done. |
| 359 if (previous != MemoryPressureLevel::kNone || | 396 if (previous != MemoryPressureLevel::kNone || |
| 360 level == MemoryPressureLevel::kNone) { | 397 level == MemoryPressureLevel::kNone) { |
| 361 return; | 398 return; |
| 362 } | 399 } |
| 400 if (trace_compiler_dispatcher_) { |
| 401 PrintF("CompilerDispatcher: received memory pressure notification\n"); |
| 402 } |
| 363 if (is_isolate_locked) { | 403 if (is_isolate_locked) { |
| 364 AbortAll(BlockingBehavior::kDontBlock); | 404 AbortAll(BlockingBehavior::kDontBlock); |
| 365 } else { | 405 } else { |
| 366 { | 406 { |
| 367 base::LockGuard<base::Mutex> lock(&mutex_); | 407 base::LockGuard<base::Mutex> lock(&mutex_); |
| 368 if (abort_) return; | 408 if (abort_) return; |
| 369 // By going into abort mode here, and clearing the | 409 // By going into abort mode here, and clearing the |
| 370 // pending_background_jobs_, we at keep existing background jobs from | 410 // pending_background_jobs_, we at keep existing background jobs from |
| 371 // picking up more work before the MemoryPressureTask gets executed. | 411 // picking up more work before the MemoryPressureTask gets executed. |
| 372 abort_ = true; | 412 abort_ = true; |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 451 running_background_jobs_.insert(job); | 491 running_background_jobs_.insert(job); |
| 452 } | 492 } |
| 453 } | 493 } |
| 454 if (job == nullptr) return; | 494 if (job == nullptr) return; |
| 455 | 495 |
| 456 if (V8_UNLIKELY(block_for_testing_.Value())) { | 496 if (V8_UNLIKELY(block_for_testing_.Value())) { |
| 457 block_for_testing_.SetValue(false); | 497 block_for_testing_.SetValue(false); |
| 458 semaphore_for_testing_.Wait(); | 498 semaphore_for_testing_.Wait(); |
| 459 } | 499 } |
| 460 | 500 |
| 501 if (trace_compiler_dispatcher_) { |
| 502 PrintF("CompilerDispatcher: doing background work\n"); |
| 503 } |
| 504 |
| 461 DoNextStepOnBackgroundThread(job); | 505 DoNextStepOnBackgroundThread(job); |
| 462 | 506 |
| 463 ScheduleMoreBackgroundTasksIfNeeded(); | 507 ScheduleMoreBackgroundTasksIfNeeded(); |
| 464 // Unconditionally schedule an idle task, as all background steps have to be | 508 // Unconditionally schedule an idle task, as all background steps have to be |
| 465 // followed by a main thread step. | 509 // followed by a main thread step. |
| 466 ScheduleIdleTaskFromAnyThread(); | 510 ScheduleIdleTaskFromAnyThread(); |
| 467 | 511 |
| 468 { | 512 { |
| 469 base::LockGuard<base::Mutex> lock(&mutex_); | 513 base::LockGuard<base::Mutex> lock(&mutex_); |
| 470 running_background_jobs_.erase(job); | 514 running_background_jobs_.erase(job); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 500 | 544 |
| 501 // Number of jobs that are unlikely to make progress during any idle callback | 545 // Number of jobs that are unlikely to make progress during any idle callback |
| 502 // due to their estimated duration. | 546 // due to their estimated duration. |
| 503 size_t too_long_jobs = 0; | 547 size_t too_long_jobs = 0; |
| 504 | 548 |
| 505 // Iterate over all available jobs & remaining time. For each job, decide | 549 // Iterate over all available jobs & remaining time. For each job, decide |
| 506 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | 550 // whether to 1) skip it (if it would take too long), 2) erase it (if it's |
| 507 // finished), or 3) make progress on it. | 551 // finished), or 3) make progress on it. |
| 508 double idle_time_in_seconds = | 552 double idle_time_in_seconds = |
| 509 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | 553 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
| 554 |
| 555 if (trace_compiler_dispatcher_) { |
| 556 PrintF("CompilerDispatcher: received %0.1lfms of idle time\n", |
| 557 idle_time_in_seconds * |
| 558 static_cast<double>(base::Time::kMillisecondsPerSecond)); |
| 559 } |
| 510 for (auto job = jobs_.begin(); | 560 for (auto job = jobs_.begin(); |
| 511 job != jobs_.end() && idle_time_in_seconds > 0.0; | 561 job != jobs_.end() && idle_time_in_seconds > 0.0; |
| 512 idle_time_in_seconds = | 562 idle_time_in_seconds = |
| 513 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | 563 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { |
| 514 // Don't work on jobs that are being worked on by background tasks. | 564 // Don't work on jobs that are being worked on by background tasks. |
| 515 // Similarly, remove jobs we work on from the set of available background | 565 // Similarly, remove jobs we work on from the set of available background |
| 516 // jobs. | 566 // jobs. |
| 517 std::unique_ptr<base::LockGuard<base::Mutex>> lock( | 567 std::unique_ptr<base::LockGuard<base::Mutex>> lock( |
| 518 new base::LockGuard<base::Mutex>(&mutex_)); | 568 new base::LockGuard<base::Mutex>(&mutex_)); |
| 519 if (running_background_jobs_.find(job->second.get()) != | 569 if (running_background_jobs_.find(job->second.get()) != |
| (...skipping 10 matching lines...) Expand all Loading... |
| 530 // have managed to finish the job in a large idle task to assess | 580 // have managed to finish the job in a large idle task to assess |
| 531 // whether we should ask for another idle callback. | 581 // whether we should ask for another idle callback. |
| 532 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; | 582 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; |
| 533 if (it == pending_background_jobs_.end()) { | 583 if (it == pending_background_jobs_.end()) { |
| 534 lock.reset(); | 584 lock.reset(); |
| 535 ConsiderJobForBackgroundProcessing(job->second.get()); | 585 ConsiderJobForBackgroundProcessing(job->second.get()); |
| 536 } | 586 } |
| 537 ++job; | 587 ++job; |
| 538 } else if (IsFinished(job->second.get())) { | 588 } else if (IsFinished(job->second.get())) { |
| 539 DCHECK(it == pending_background_jobs_.end()); | 589 DCHECK(it == pending_background_jobs_.end()); |
| 590 if (trace_compiler_dispatcher_) { |
| 591 PrintF("CompilerDispatcher: finished working on "); |
| 592 job->second->ShortPrint(); |
| 593 PrintF(": %s\n", job->second->status() == CompileJobStatus::kDone |
| 594 ? "success" |
| 595 : "failure"); |
| 596 tracer_->DumpStatistics(); |
| 597 } |
| 540 job->second->ResetOnMainThread(); | 598 job->second->ResetOnMainThread(); |
| 541 job = jobs_.erase(job); | 599 job = jobs_.erase(job); |
| 542 continue; | 600 continue; |
| 543 } else { | 601 } else { |
| 544 // Do one step, and keep processing the job (as we don't advance the | 602 // Do one step, and keep processing the job (as we don't advance the |
| 545 // iterator). | 603 // iterator). |
| 546 if (it != pending_background_jobs_.end()) { | 604 if (it != pending_background_jobs_.end()) { |
| 547 pending_background_jobs_.erase(it); | 605 pending_background_jobs_.erase(it); |
| 548 } | 606 } |
| 549 lock.reset(); | 607 lock.reset(); |
| 550 DoNextStepOnMainThread(isolate_, job->second.get(), | 608 DoNextStepOnMainThread(isolate_, job->second.get(), |
| 551 ExceptionHandling::kSwallow); | 609 ExceptionHandling::kSwallow); |
| 552 } | 610 } |
| 553 } | 611 } |
| 554 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 612 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
| 555 } | 613 } |
| 556 | 614 |
| 557 } // namespace internal | 615 } // namespace internal |
| 558 } // namespace v8 | 616 } // namespace v8 |
| OLD | NEW |