Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(585)

Side by Side Diff: third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc

Issue 2533603002: [scheduler] Add options to TaskQueue::InsertFence (Closed)
Patch Set: Addressed comments Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 double cpu_percentage) { 92 double cpu_percentage) {
93 Advance(now); 93 Advance(now);
94 cpu_percentage_ = cpu_percentage; 94 cpu_percentage_ = cpu_percentage;
95 EnforceBudgetLevelRestrictions(); 95 EnforceBudgetLevelRestrictions();
96 } 96 }
97 97
98 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, 98 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now,
99 TaskQueue* queue) { 99 TaskQueue* queue) {
100 std::pair<TaskQueueMap::iterator, bool> insert_result = 100 std::pair<TaskQueueMap::iterator, bool> insert_result =
101 task_queue_throttler_->queue_details_.insert( 101 task_queue_throttler_->queue_details_.insert(
102 std::make_pair(queue, Metadata(0, queue->IsQueueEnabled()))); 102 std::make_pair(queue, Metadata()));
103 Metadata& metadata = insert_result.first->second; 103 Metadata& metadata = insert_result.first->second;
104 DCHECK(!metadata.time_budget_pool); 104 DCHECK(!metadata.time_budget_pool);
105 metadata.time_budget_pool = this; 105 metadata.time_budget_pool = this;
106 106
107 associated_task_queues_.insert(queue); 107 associated_task_queues_.insert(queue);
108 108
109 if (!is_enabled_ || !task_queue_throttler_->IsThrottled(queue)) 109 if (!is_enabled_ || !task_queue_throttler_->IsThrottled(queue))
110 return; 110 return;
111 111
112 queue->SetQueueEnabled(false); 112 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
113 113
114 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, 114 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue,
115 GetNextAllowedRunTime()); 115 GetNextAllowedRunTime());
116 } 116 }
117 117
118 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now, 118 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now,
119 TaskQueue* queue) { 119 TaskQueue* queue) {
120 auto find_it = task_queue_throttler_->queue_details_.find(queue); 120 auto find_it = task_queue_throttler_->queue_details_.find(queue);
121 DCHECK(find_it != task_queue_throttler_->queue_details_.end() && 121 DCHECK(find_it != task_queue_throttler_->queue_details_.end() &&
122 find_it->second.time_budget_pool == this); 122 find_it->second.time_budget_pool == this);
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 last_checkpoint_ = now; 254 last_checkpoint_ = now;
255 } 255 }
256 } 256 }
257 257
258 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues( 258 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues(
259 base::TimeTicks now) { 259 base::TimeTicks now) {
260 for (TaskQueue* queue : associated_task_queues_) { 260 for (TaskQueue* queue : associated_task_queues_) {
261 if (!task_queue_throttler_->IsThrottled(queue)) 261 if (!task_queue_throttler_->IsThrottled(queue))
262 continue; 262 continue;
263 263
264 queue->SetQueueEnabled(false); 264 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
265 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, 265 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue,
266 base::nullopt); 266 base::nullopt);
267 } 267 }
268 } 268 }
269 269
270 void TaskQueueThrottler::TimeBudgetPool::EnforceBudgetLevelRestrictions() { 270 void TaskQueueThrottler::TimeBudgetPool::EnforceBudgetLevelRestrictions() {
271 if (max_budget_level_) { 271 if (max_budget_level_) {
272 current_budget_level_ = 272 current_budget_level_ =
273 std::min(current_budget_level_, max_budget_level_.value()); 273 std::min(current_budget_level_, max_budget_level_.value());
274 } 274 }
275 if (max_throttling_duration_) { 275 if (max_throttling_duration_) {
276 // Current budget level may be negative. 276 // Current budget level may be negative.
277 current_budget_level_ = 277 current_budget_level_ =
278 std::max(current_budget_level_, 278 std::max(current_budget_level_,
279 -max_throttling_duration_.value() * cpu_percentage_); 279 -max_throttling_duration_.value() * cpu_percentage_);
280 } 280 }
281 } 281 }
282 282
283 // TODO(altimin): Control max_budget_level and max_throttling_duration
284 // from Finch.
285 TaskQueueThrottler::TaskQueueThrottler( 283 TaskQueueThrottler::TaskQueueThrottler(
286 RendererSchedulerImpl* renderer_scheduler, 284 RendererSchedulerImpl* renderer_scheduler,
287 const char* tracing_category) 285 const char* tracing_category)
288 : task_runner_(renderer_scheduler->ControlTaskRunner()), 286 : task_runner_(renderer_scheduler->ControlTaskRunner()),
289 renderer_scheduler_(renderer_scheduler), 287 renderer_scheduler_(renderer_scheduler),
290 tick_clock_(renderer_scheduler->tick_clock()), 288 tick_clock_(renderer_scheduler->tick_clock()),
291 tracing_category_(tracing_category), 289 tracing_category_(tracing_category),
292 time_domain_(new ThrottledTimeDomain(this, tracing_category)), 290 time_domain_(new ThrottledTimeDomain(this, tracing_category)),
293 allow_throttling_(true), 291 allow_throttling_(true),
294 weak_factory_(this) { 292 weak_factory_(this) {
(...skipping 13 matching lines...) Expand all
308 TaskQueue* task_queue = map_entry.first; 306 TaskQueue* task_queue = map_entry.first;
309 if (IsThrottled(task_queue)) { 307 if (IsThrottled(task_queue)) {
310 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); 308 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
311 task_queue->RemoveFence(); 309 task_queue->RemoveFence();
312 } 310 }
313 } 311 }
314 312
315 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get()); 313 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get());
316 } 314 }
317 315
318 void TaskQueueThrottler::SetQueueEnabled(TaskQueue* task_queue, bool enabled) {
319 // Both the TaskQueueThrottler and other systems want to enable and disable
320 // task queues. The policy we've adopted to deal with this is:
321 //
322 // A task queue is disabled if either the TaskQueueThrottler or the caller
323 // vote to disable it.
324 //
325 // A task queue is enabled only if both the TaskQueueThrottler and the caller
326 // vote to enable it.
327 TaskQueueMap::iterator find_it = queue_details_.find(task_queue);
328
329 // If TaskQueueThrottler does not know about this queue, just call
330 // SetQueueEnabled directly.
331 if (find_it == queue_details_.end()) {
332 task_queue->SetQueueEnabled(enabled);
333 return;
334 }
335
336 // Remember the caller's preference so we know what to do when the task queue
337 // isn't throttled, or has budget to run.
338 find_it->second.enabled = enabled;
339
340 if (!IsThrottled(task_queue)) {
341 task_queue->SetQueueEnabled(enabled);
342 return;
343 }
344
345 if (enabled) {
346 // If the task queue is throttled and we want to enable it, we can't
347 // do it immediately due to task alignment requirement and we should
348 // schedule a call to PumpThrottledTasks.
349 MaybeSchedulePumpQueue(FROM_HERE, tick_clock_->NowTicks(), task_queue,
350 base::nullopt);
351 } else {
352 task_queue->SetQueueEnabled(false);
353 }
354 }
355
356 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) { 316 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) {
357 DCHECK_NE(task_queue, task_runner_.get()); 317 DCHECK_NE(task_queue, task_runner_.get());
358 318
359 std::pair<TaskQueueMap::iterator, bool> insert_result = queue_details_.insert( 319 std::pair<TaskQueueMap::iterator, bool> insert_result =
360 std::make_pair(task_queue, Metadata(0 /* ref_count */, 320 queue_details_.insert(std::make_pair(task_queue, Metadata()));
361 task_queue->IsQueueEnabled()))); 321 insert_result.first->second.throttling_ref_count++;
362 322
363 if (insert_result.first->second.throttling_ref_count == 0) { 323 if (!allow_throttling_)
364 if (allow_throttling_) { 324 return;
365 task_queue->SetTimeDomain(time_domain_.get());
366 task_queue->RemoveFence();
367 task_queue->SetQueueEnabled(false);
368 325
369 if (!task_queue->IsEmpty()) { 326 // If ref_count is 1, the task queue is newly throttled.
370 if (task_queue->HasPendingImmediateWork()) { 327 if (insert_result.first->second.throttling_ref_count != 1)
371 OnTimeDomainHasImmediateWork(task_queue); 328 return;
372 } else { 329
373 OnTimeDomainHasDelayedWork(task_queue); 330 task_queue->SetTimeDomain(time_domain_.get());
374 } 331 // This blocks any tasks from |task_queue| until PumpThrottledTasks() to
375 } 332 // enforce task alignment.
333 task_queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
334
335 if (!task_queue->IsEmpty()) {
336 if (task_queue->HasPendingImmediateWork()) {
337 OnTimeDomainHasImmediateWork(task_queue);
338 } else {
339 OnTimeDomainHasDelayedWork(task_queue);
376 } 340 }
377
378 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
379 "task_queue", task_queue);
380 } 341 }
381 342
382 insert_result.first->second.throttling_ref_count++; 343 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
Sami 2016/11/25 17:12:59 Could you keep this at the top of the function so
altimin 2016/11/25 17:44:10 I want to emit this only when we are actually thro
344 "task_queue", task_queue);
383 } 345 }
384 346
385 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) { 347 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
386 TaskQueueMap::iterator iter = queue_details_.find(task_queue); 348 TaskQueueMap::iterator iter = queue_details_.find(task_queue);
387 349
388 if (iter != queue_details_.end() && 350 if (iter != queue_details_.end() &&
389 --iter->second.throttling_ref_count == 0) { 351 --iter->second.throttling_ref_count == 0) {
390 bool enabled = iter->second.enabled;
391
392 MaybeDeleteQueueMetadata(iter); 352 MaybeDeleteQueueMetadata(iter);
393 353
394 if (allow_throttling_) { 354 if (allow_throttling_) {
395 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); 355 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
396 task_queue->RemoveFence(); 356 task_queue->RemoveFence();
397 task_queue->SetQueueEnabled(enabled);
398 } 357 }
399 358
400 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled", 359 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled",
401 "task_queue", task_queue); 360 "task_queue", task_queue);
402 } 361 }
403 } 362 }
404 363
405 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const { 364 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const {
406 if (!allow_throttling_) 365 if (!allow_throttling_)
407 return false; 366 return false;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 414
456 void TaskQueueThrottler::PumpThrottledTasks() { 415 void TaskQueueThrottler::PumpThrottledTasks() {
457 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks"); 416 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks");
458 pending_pump_throttled_tasks_runtime_.reset(); 417 pending_pump_throttled_tasks_runtime_.reset();
459 418
460 LazyNow lazy_now(tick_clock_); 419 LazyNow lazy_now(tick_clock_);
461 base::Optional<base::TimeTicks> next_scheduled_delayed_task; 420 base::Optional<base::TimeTicks> next_scheduled_delayed_task;
462 421
463 for (const TaskQueueMap::value_type& map_entry : queue_details_) { 422 for (const TaskQueueMap::value_type& map_entry : queue_details_) {
464 TaskQueue* task_queue = map_entry.first; 423 TaskQueue* task_queue = map_entry.first;
465 if (!map_entry.second.enabled || task_queue->IsEmpty() || 424 if (task_queue->IsEmpty() || !IsThrottled(task_queue))
466 !IsThrottled(task_queue))
467 continue; 425 continue;
468 426
469 // Don't enable queues whose budget pool doesn't allow them to run now. 427 // Don't enable queues whose budget pool doesn't allow them to run now.
470 base::TimeTicks next_allowed_run_time = 428 base::TimeTicks next_allowed_run_time =
471 GetNextAllowedRunTime(lazy_now.Now(), task_queue); 429 GetNextAllowedRunTime(lazy_now.Now(), task_queue);
472 base::Optional<base::TimeTicks> next_desired_run_time = 430 base::Optional<base::TimeTicks> next_desired_run_time =
473 NextTaskRunTime(&lazy_now, task_queue); 431 NextTaskRunTime(&lazy_now, task_queue);
474 432
475 if (next_desired_run_time && 433 if (next_desired_run_time &&
476 next_allowed_run_time > next_desired_run_time.value()) { 434 next_allowed_run_time > next_desired_run_time.value()) {
477 TRACE_EVENT1( 435 TRACE_EVENT1(
478 tracing_category_, 436 tracing_category_,
479 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled", 437 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
480 "throttle_time_in_seconds", 438 "throttle_time_in_seconds",
481 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF()); 439 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF());
482 440
483 // Schedule a pump for queue which was disabled because of time budget. 441 // Schedule a pump for queue which was disabled because of time budget.
484 next_scheduled_delayed_task = 442 next_scheduled_delayed_task =
485 Min(next_scheduled_delayed_task, next_allowed_run_time); 443 Min(next_scheduled_delayed_task, next_allowed_run_time);
486 444
487 continue; 445 continue;
488 } 446 }
489 447
490 next_scheduled_delayed_task = 448 next_scheduled_delayed_task =
491 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp()); 449 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp());
492 450
493 if (next_allowed_run_time > lazy_now.Now()) 451 if (next_allowed_run_time > lazy_now.Now())
494 continue; 452 continue;
495 453
496 task_queue->SetQueueEnabled(true); 454 // Remove previous fence and install a new one, allowing all tasks posted
497 task_queue->InsertFence(); 455 // on |task_queue| up until this point to run and block all further tasks.
456 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
498 } 457 }
499 458
500 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is 459 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is
501 // a pending delayed task or a throttled task ready to run. 460 // a pending delayed task or a throttled task ready to run.
502 // NOTE: posting a non-delayed task in the future will result in 461 // NOTE: posting a non-delayed task in the future will result in
503 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called. 462 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called.
504 if (next_scheduled_delayed_task) { 463 if (next_scheduled_delayed_task) {
505 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(), 464 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(),
506 *next_scheduled_delayed_task); 465 *next_scheduled_delayed_task);
507 } 466 }
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 pool->AsValueInto(state, now); 545 pool->AsValueInto(state, now);
587 } 546 }
588 state->EndDictionary(); 547 state->EndDictionary();
589 548
590 state->BeginDictionary("queue_details"); 549 state->BeginDictionary("queue_details");
591 for (const auto& map_entry : queue_details_) { 550 for (const auto& map_entry : queue_details_) {
592 state->BeginDictionaryWithCopiedName(PointerToId(map_entry.first)); 551 state->BeginDictionaryWithCopiedName(PointerToId(map_entry.first));
593 552
594 state->SetInteger("throttling_ref_count", 553 state->SetInteger("throttling_ref_count",
595 map_entry.second.throttling_ref_count); 554 map_entry.second.throttling_ref_count);
596 state->SetBoolean("enabled", map_entry.second.enabled);
597 555
598 state->EndDictionary(); 556 state->EndDictionary();
599 } 557 }
600 state->EndDictionary(); 558 state->EndDictionary();
601 } 559 }
602 560
603 TaskQueueThrottler::TimeBudgetPool* 561 TaskQueueThrottler::TimeBudgetPool*
604 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) { 562 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) {
605 auto find_it = queue_details_.find(queue); 563 auto find_it = queue_details_.find(queue);
606 if (find_it == queue_details_.end()) 564 if (find_it == queue_details_.end())
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
643 601
644 for (const auto& map_entry : queue_details_) { 602 for (const auto& map_entry : queue_details_) {
645 if (map_entry.second.throttling_ref_count == 0) 603 if (map_entry.second.throttling_ref_count == 0)
646 continue; 604 continue;
647 605
648 TaskQueue* queue = map_entry.first; 606 TaskQueue* queue = map_entry.first;
649 607
650 queue->SetTimeDomain(renderer_scheduler_->GetActiveTimeDomain()); 608 queue->SetTimeDomain(renderer_scheduler_->GetActiveTimeDomain());
651 609
652 queue->RemoveFence(); 610 queue->RemoveFence();
653 queue->SetQueueEnabled(map_entry.second.enabled);
654 } 611 }
655 612
656 pump_throttled_tasks_closure_.Cancel(); 613 pump_throttled_tasks_closure_.Cancel();
657 pending_pump_throttled_tasks_runtime_ = base::nullopt; 614 pending_pump_throttled_tasks_runtime_ = base::nullopt;
658 615
659 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_DisableThrottling"); 616 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_DisableThrottling");
660 } 617 }
661 618
662 void TaskQueueThrottler::EnableThrottling() { 619 void TaskQueueThrottler::EnableThrottling() {
663 if (allow_throttling_) 620 if (allow_throttling_)
664 return; 621 return;
665 622
666 allow_throttling_ = true; 623 allow_throttling_ = true;
667 624
668 LazyNow lazy_now(tick_clock_); 625 LazyNow lazy_now(tick_clock_);
669 626
670 for (const auto& map_entry : queue_details_) { 627 for (const auto& map_entry : queue_details_) {
671 if (map_entry.second.throttling_ref_count == 0) 628 if (map_entry.second.throttling_ref_count == 0)
672 continue; 629 continue;
673 630
674 TaskQueue* queue = map_entry.first; 631 TaskQueue* queue = map_entry.first;
675 632
676 queue->SetQueueEnabled(false); 633 // Throttling is enabled and task queue should be blocked immediately
634 // to enforce task alignment.
635 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
677 queue->SetTimeDomain(time_domain_.get()); 636 queue->SetTimeDomain(time_domain_.get());
678 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue, 637 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue,
679 GetNextAllowedRunTime(lazy_now.Now(), queue)); 638 GetNextAllowedRunTime(lazy_now.Now(), queue));
680 } 639 }
681 640
682 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling"); 641 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling");
683 } 642 }
684 643
685 } // namespace scheduler 644 } // namespace scheduler
686 } // namespace blink 645 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698