| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium 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 "platform/scheduler/renderer/task_queue_throttler.h" | 5 #include "platform/scheduler/renderer/task_queue_throttler.h" |
| 6 | 6 |
| 7 #include <cstdint> | 7 #include <cstdint> |
| 8 | 8 |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 double cpu_percentage) { | 86 double cpu_percentage) { |
| 87 Advance(now); | 87 Advance(now); |
| 88 cpu_percentage_ = cpu_percentage; | 88 cpu_percentage_ = cpu_percentage; |
| 89 EnforceBudgetLevelRestrictions(); | 89 EnforceBudgetLevelRestrictions(); |
| 90 } | 90 } |
| 91 | 91 |
| 92 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, | 92 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, |
| 93 TaskQueue* queue) { | 93 TaskQueue* queue) { |
| 94 std::pair<TaskQueueMap::iterator, bool> insert_result = | 94 std::pair<TaskQueueMap::iterator, bool> insert_result = |
| 95 task_queue_throttler_->queue_details_.insert( | 95 task_queue_throttler_->queue_details_.insert( |
| 96 std::make_pair(queue, Metadata(0, queue->IsQueueEnabled()))); | 96 std::make_pair(queue, Metadata())); |
| 97 Metadata& metadata = insert_result.first->second; | 97 Metadata& metadata = insert_result.first->second; |
| 98 DCHECK(!metadata.time_budget_pool); | 98 DCHECK(!metadata.time_budget_pool); |
| 99 metadata.time_budget_pool = this; | 99 metadata.time_budget_pool = this; |
| 100 | 100 |
| 101 associated_task_queues_.insert(queue); | 101 associated_task_queues_.insert(queue); |
| 102 | 102 |
| 103 if (!is_enabled_ || !task_queue_throttler_->IsThrottled(queue)) | 103 if (!is_enabled_ || !task_queue_throttler_->IsThrottled(queue)) |
| 104 return; | 104 return; |
| 105 | 105 |
| 106 queue->SetQueueEnabled(false); | 106 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); |
| 107 | 107 |
| 108 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | 108 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, |
| 109 GetNextAllowedRunTime()); | 109 GetNextAllowedRunTime()); |
| 110 } | 110 } |
| 111 | 111 |
| 112 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now, | 112 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now, |
| 113 TaskQueue* queue) { | 113 TaskQueue* queue) { |
| 114 auto find_it = task_queue_throttler_->queue_details_.find(queue); | 114 auto find_it = task_queue_throttler_->queue_details_.find(queue); |
| 115 DCHECK(find_it != task_queue_throttler_->queue_details_.end() && | 115 DCHECK(find_it != task_queue_throttler_->queue_details_.end() && |
| 116 find_it->second.time_budget_pool == this); | 116 find_it->second.time_budget_pool == this); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 last_checkpoint_ = now; | 242 last_checkpoint_ = now; |
| 243 } | 243 } |
| 244 } | 244 } |
| 245 | 245 |
| 246 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues( | 246 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues( |
| 247 base::TimeTicks now) { | 247 base::TimeTicks now) { |
| 248 for (TaskQueue* queue : associated_task_queues_) { | 248 for (TaskQueue* queue : associated_task_queues_) { |
| 249 if (!task_queue_throttler_->IsThrottled(queue)) | 249 if (!task_queue_throttler_->IsThrottled(queue)) |
| 250 continue; | 250 continue; |
| 251 | 251 |
| 252 queue->SetQueueEnabled(false); | 252 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); |
| 253 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | 253 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, |
| 254 base::nullopt); | 254 base::nullopt); |
| 255 } | 255 } |
| 256 } | 256 } |
| 257 | 257 |
| 258 void TaskQueueThrottler::TimeBudgetPool::EnforceBudgetLevelRestrictions() { | 258 void TaskQueueThrottler::TimeBudgetPool::EnforceBudgetLevelRestrictions() { |
| 259 if (max_budget_level_) { | 259 if (max_budget_level_) { |
| 260 current_budget_level_ = | 260 current_budget_level_ = |
| 261 std::min(current_budget_level_, max_budget_level_.value()); | 261 std::min(current_budget_level_, max_budget_level_.value()); |
| 262 } | 262 } |
| 263 if (max_throttling_duration_) { | 263 if (max_throttling_duration_) { |
| 264 // Current budget level may be negative. | 264 // Current budget level may be negative. |
| 265 current_budget_level_ = | 265 current_budget_level_ = |
| 266 std::max(current_budget_level_, | 266 std::max(current_budget_level_, |
| 267 -max_throttling_duration_.value() * cpu_percentage_); | 267 -max_throttling_duration_.value() * cpu_percentage_); |
| 268 } | 268 } |
| 269 } | 269 } |
| 270 | 270 |
| 271 // TODO(altimin): Control max_budget_level and max_throttling_duration | |
| 272 // from Finch. | |
| 273 TaskQueueThrottler::TaskQueueThrottler( | 271 TaskQueueThrottler::TaskQueueThrottler( |
| 274 RendererSchedulerImpl* renderer_scheduler, | 272 RendererSchedulerImpl* renderer_scheduler, |
| 275 const char* tracing_category) | 273 const char* tracing_category) |
| 276 : task_runner_(renderer_scheduler->ControlTaskRunner()), | 274 : task_runner_(renderer_scheduler->ControlTaskRunner()), |
| 277 renderer_scheduler_(renderer_scheduler), | 275 renderer_scheduler_(renderer_scheduler), |
| 278 tick_clock_(renderer_scheduler->tick_clock()), | 276 tick_clock_(renderer_scheduler->tick_clock()), |
| 279 tracing_category_(tracing_category), | 277 tracing_category_(tracing_category), |
| 280 time_domain_(new ThrottledTimeDomain(this, tracing_category)), | 278 time_domain_(new ThrottledTimeDomain(this, tracing_category)), |
| 281 allow_throttling_(true), | 279 allow_throttling_(true), |
| 282 weak_factory_(this) { | 280 weak_factory_(this) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 296 TaskQueue* task_queue = map_entry.first; | 294 TaskQueue* task_queue = map_entry.first; |
| 297 if (IsThrottled(task_queue)) { | 295 if (IsThrottled(task_queue)) { |
| 298 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); | 296 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); |
| 299 task_queue->RemoveFence(); | 297 task_queue->RemoveFence(); |
| 300 } | 298 } |
| 301 } | 299 } |
| 302 | 300 |
| 303 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get()); | 301 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get()); |
| 304 } | 302 } |
| 305 | 303 |
| 306 void TaskQueueThrottler::SetQueueEnabled(TaskQueue* task_queue, bool enabled) { | |
| 307 // Both the TaskQueueThrottler and other systems want to enable and disable | |
| 308 // task queues. The policy we've adopted to deal with this is: | |
| 309 // | |
| 310 // A task queue is disabled if either the TaskQueueThrottler or the caller | |
| 311 // vote to disable it. | |
| 312 // | |
| 313 // A task queue is enabled only if both the TaskQueueThrottler and the caller | |
| 314 // vote to enable it. | |
| 315 TaskQueueMap::iterator find_it = queue_details_.find(task_queue); | |
| 316 | |
| 317 // If TaskQueueThrottler does not know about this queue, just call | |
| 318 // SetQueueEnabled directly. | |
| 319 if (find_it == queue_details_.end()) { | |
| 320 task_queue->SetQueueEnabled(enabled); | |
| 321 return; | |
| 322 } | |
| 323 | |
| 324 // Remember the caller's preference so we know what to do when the task queue | |
| 325 // isn't throttled, or has budget to run. | |
| 326 find_it->second.enabled = enabled; | |
| 327 | |
| 328 if (!IsThrottled(task_queue)) { | |
| 329 task_queue->SetQueueEnabled(enabled); | |
| 330 return; | |
| 331 } | |
| 332 | |
| 333 if (enabled) { | |
| 334 // If the task queue is throttled and we want to enable it, we can't | |
| 335 // do it immediately due to task alignment requirement and we should | |
| 336 // schedule a call to PumpThrottledTasks. | |
| 337 MaybeSchedulePumpQueue(FROM_HERE, tick_clock_->NowTicks(), task_queue, | |
| 338 base::nullopt); | |
| 339 } else { | |
| 340 task_queue->SetQueueEnabled(false); | |
| 341 } | |
| 342 } | |
| 343 | |
| 344 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) { | 304 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) { |
| 345 DCHECK_NE(task_queue, task_runner_.get()); | 305 DCHECK_NE(task_queue, task_runner_.get()); |
| 346 | 306 |
| 347 std::pair<TaskQueueMap::iterator, bool> insert_result = queue_details_.insert( | 307 std::pair<TaskQueueMap::iterator, bool> insert_result = |
| 348 std::make_pair(task_queue, Metadata(0 /* ref_count */, | 308 queue_details_.insert(std::make_pair(task_queue, Metadata())); |
| 349 task_queue->IsQueueEnabled()))); | 309 insert_result.first->second.throttling_ref_count++; |
| 350 | 310 |
| 351 if (insert_result.first->second.throttling_ref_count == 0) { | 311 // If ref_count is 1, the task queue is newly throttled. |
| 352 if (allow_throttling_) { | 312 if (insert_result.first->second.throttling_ref_count != 1) |
| 353 task_queue->SetTimeDomain(time_domain_.get()); | 313 return; |
| 354 task_queue->RemoveFence(); | |
| 355 task_queue->SetQueueEnabled(false); | |
| 356 | 314 |
| 357 if (!task_queue->IsEmpty()) { | 315 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled", |
| 358 if (task_queue->HasPendingImmediateWork()) { | 316 "task_queue", task_queue); |
| 359 OnTimeDomainHasImmediateWork(task_queue); | 317 |
| 360 } else { | 318 if (!allow_throttling_) |
| 361 OnTimeDomainHasDelayedWork(task_queue); | 319 return; |
| 362 } | 320 |
| 363 } | 321 task_queue->SetTimeDomain(time_domain_.get()); |
| 322 // This blocks any tasks from |task_queue| until PumpThrottledTasks() to |
| 323 // enforce task alignment. |
| 324 task_queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); |
| 325 |
| 326 if (!task_queue->IsEmpty()) { |
| 327 if (task_queue->HasPendingImmediateWork()) { |
| 328 OnTimeDomainHasImmediateWork(task_queue); |
| 329 } else { |
| 330 OnTimeDomainHasDelayedWork(task_queue); |
| 364 } | 331 } |
| 365 | |
| 366 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled", | |
| 367 "task_queue", task_queue); | |
| 368 } | 332 } |
| 369 | |
| 370 insert_result.first->second.throttling_ref_count++; | |
| 371 } | 333 } |
| 372 | 334 |
| 373 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) { | 335 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) { |
| 374 TaskQueueMap::iterator iter = queue_details_.find(task_queue); | 336 TaskQueueMap::iterator iter = queue_details_.find(task_queue); |
| 375 | 337 |
| 376 if (iter != queue_details_.end() && | 338 if (iter == queue_details_.end() || |
| 377 --iter->second.throttling_ref_count == 0) { | 339 --iter->second.throttling_ref_count != 0) { |
| 378 bool enabled = iter->second.enabled; | 340 return; |
| 341 } |
| 379 | 342 |
| 380 MaybeDeleteQueueMetadata(iter); | 343 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled", |
| 344 "task_queue", task_queue); |
| 381 | 345 |
| 382 if (allow_throttling_) { | 346 MaybeDeleteQueueMetadata(iter); |
| 383 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); | |
| 384 task_queue->RemoveFence(); | |
| 385 task_queue->SetQueueEnabled(enabled); | |
| 386 } | |
| 387 | 347 |
| 388 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUntrottled", | 348 if (!allow_throttling_) |
| 389 "task_queue", task_queue); | 349 return; |
| 390 } | 350 |
| 351 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); |
| 352 task_queue->RemoveFence(); |
| 391 } | 353 } |
| 392 | 354 |
| 393 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const { | 355 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const { |
| 394 if (!allow_throttling_) | 356 if (!allow_throttling_) |
| 395 return false; | 357 return false; |
| 396 | 358 |
| 397 auto find_it = queue_details_.find(task_queue); | 359 auto find_it = queue_details_.find(task_queue); |
| 398 if (find_it == queue_details_.end()) | 360 if (find_it == queue_details_.end()) |
| 399 return false; | 361 return false; |
| 400 return find_it->second.throttling_ref_count > 0; | 362 return find_it->second.throttling_ref_count > 0; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 443 | 405 |
| 444 void TaskQueueThrottler::PumpThrottledTasks() { | 406 void TaskQueueThrottler::PumpThrottledTasks() { |
| 445 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks"); | 407 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks"); |
| 446 pending_pump_throttled_tasks_runtime_.reset(); | 408 pending_pump_throttled_tasks_runtime_.reset(); |
| 447 | 409 |
| 448 LazyNow lazy_now(tick_clock_); | 410 LazyNow lazy_now(tick_clock_); |
| 449 base::Optional<base::TimeTicks> next_scheduled_delayed_task; | 411 base::Optional<base::TimeTicks> next_scheduled_delayed_task; |
| 450 | 412 |
| 451 for (const TaskQueueMap::value_type& map_entry : queue_details_) { | 413 for (const TaskQueueMap::value_type& map_entry : queue_details_) { |
| 452 TaskQueue* task_queue = map_entry.first; | 414 TaskQueue* task_queue = map_entry.first; |
| 453 if (!map_entry.second.enabled || task_queue->IsEmpty() || | 415 if (task_queue->IsEmpty() || !IsThrottled(task_queue)) |
| 454 !IsThrottled(task_queue)) | |
| 455 continue; | 416 continue; |
| 456 | 417 |
| 457 // Don't enable queues whose budget pool doesn't allow them to run now. | 418 // Don't enable queues whose budget pool doesn't allow them to run now. |
| 458 base::TimeTicks next_allowed_run_time = | 419 base::TimeTicks next_allowed_run_time = |
| 459 GetNextAllowedRunTime(lazy_now.Now(), task_queue); | 420 GetNextAllowedRunTime(lazy_now.Now(), task_queue); |
| 460 base::Optional<base::TimeTicks> next_desired_run_time = | 421 base::Optional<base::TimeTicks> next_desired_run_time = |
| 461 NextTaskRunTime(&lazy_now, task_queue); | 422 NextTaskRunTime(&lazy_now, task_queue); |
| 462 | 423 |
| 463 if (next_desired_run_time && | 424 if (next_desired_run_time && |
| 464 next_allowed_run_time > next_desired_run_time.value()) { | 425 next_allowed_run_time > next_desired_run_time.value()) { |
| 465 TRACE_EVENT1( | 426 TRACE_EVENT1( |
| 466 tracing_category_, | 427 tracing_category_, |
| 467 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled", | 428 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled", |
| 468 "throttle_time_in_seconds", | 429 "throttle_time_in_seconds", |
| 469 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF()); | 430 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF()); |
| 470 | 431 |
| 471 // Schedule a pump for queue which was disabled because of time budget. | 432 // Schedule a pump for queue which was disabled because of time budget. |
| 472 next_scheduled_delayed_task = | 433 next_scheduled_delayed_task = |
| 473 Min(next_scheduled_delayed_task, next_allowed_run_time); | 434 Min(next_scheduled_delayed_task, next_allowed_run_time); |
| 474 | 435 |
| 475 continue; | 436 continue; |
| 476 } | 437 } |
| 477 | 438 |
| 478 next_scheduled_delayed_task = | 439 next_scheduled_delayed_task = |
| 479 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp()); | 440 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp()); |
| 480 | 441 |
| 481 if (next_allowed_run_time > lazy_now.Now()) | 442 if (next_allowed_run_time > lazy_now.Now()) |
| 482 continue; | 443 continue; |
| 483 | 444 |
| 484 task_queue->SetQueueEnabled(true); | 445 // Remove previous fence and install a new one, allowing all tasks posted |
| 485 task_queue->InsertFence(); | 446 // on |task_queue| up until this point to run and block all further tasks. |
| 447 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW); |
| 486 } | 448 } |
| 487 | 449 |
| 488 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is | 450 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is |
| 489 // a pending delayed task or a throttled task ready to run. | 451 // a pending delayed task or a throttled task ready to run. |
| 490 // NOTE: posting a non-delayed task in the future will result in | 452 // NOTE: posting a non-delayed task in the future will result in |
| 491 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called. | 453 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called. |
| 492 if (next_scheduled_delayed_task) { | 454 if (next_scheduled_delayed_task) { |
| 493 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(), | 455 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(), |
| 494 *next_scheduled_delayed_task); | 456 *next_scheduled_delayed_task); |
| 495 } | 457 } |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 619 | 581 |
| 620 for (const auto& map_entry : queue_details_) { | 582 for (const auto& map_entry : queue_details_) { |
| 621 if (map_entry.second.throttling_ref_count == 0) | 583 if (map_entry.second.throttling_ref_count == 0) |
| 622 continue; | 584 continue; |
| 623 | 585 |
| 624 TaskQueue* queue = map_entry.first; | 586 TaskQueue* queue = map_entry.first; |
| 625 | 587 |
| 626 queue->SetTimeDomain(renderer_scheduler_->GetActiveTimeDomain()); | 588 queue->SetTimeDomain(renderer_scheduler_->GetActiveTimeDomain()); |
| 627 | 589 |
| 628 queue->RemoveFence(); | 590 queue->RemoveFence(); |
| 629 queue->SetQueueEnabled(map_entry.second.enabled); | |
| 630 } | 591 } |
| 631 | 592 |
| 632 pump_throttled_tasks_closure_.Cancel(); | 593 pump_throttled_tasks_closure_.Cancel(); |
| 633 pending_pump_throttled_tasks_runtime_ = base::nullopt; | 594 pending_pump_throttled_tasks_runtime_ = base::nullopt; |
| 634 } | 595 } |
| 635 | 596 |
| 636 void TaskQueueThrottler::EnableThrottling() { | 597 void TaskQueueThrottler::EnableThrottling() { |
| 637 if (allow_throttling_) | 598 if (allow_throttling_) |
| 638 return; | 599 return; |
| 639 | 600 |
| 640 allow_throttling_ = true; | 601 allow_throttling_ = true; |
| 641 | 602 |
| 642 LazyNow lazy_now(tick_clock_); | 603 LazyNow lazy_now(tick_clock_); |
| 643 | 604 |
| 644 for (const auto& map_entry : queue_details_) { | 605 for (const auto& map_entry : queue_details_) { |
| 645 if (map_entry.second.throttling_ref_count == 0) | 606 if (map_entry.second.throttling_ref_count == 0) |
| 646 continue; | 607 continue; |
| 647 | 608 |
| 648 TaskQueue* queue = map_entry.first; | 609 TaskQueue* queue = map_entry.first; |
| 649 | 610 |
| 650 queue->SetQueueEnabled(false); | 611 // Throttling is enabled and task queue should be blocked immediately |
| 612 // to enforce task alignment. |
| 613 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); |
| 651 queue->SetTimeDomain(time_domain_.get()); | 614 queue->SetTimeDomain(time_domain_.get()); |
| 652 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue, | 615 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue, |
| 653 GetNextAllowedRunTime(lazy_now.Now(), queue)); | 616 GetNextAllowedRunTime(lazy_now.Now(), queue)); |
| 654 } | 617 } |
| 655 } | 618 } |
| 656 | 619 |
| 657 } // namespace scheduler | 620 } // namespace scheduler |
| 658 } // namespace blink | 621 } // namespace blink |
| OLD | NEW |