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

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: More nits 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 ref_count is 1, the task queue is newly throttled.
364 if (allow_throttling_) { 324 if (insert_result.first->second.throttling_ref_count != 1)
365 task_queue->SetTimeDomain(time_domain_.get()); 325 return;
366 task_queue->RemoveFence();
367 task_queue->SetQueueEnabled(false);
368 326
369 if (!task_queue->IsEmpty()) { 327 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
370 if (task_queue->HasPendingImmediateWork()) { 328 "task_queue", task_queue);
371 OnTimeDomainHasImmediateWork(task_queue); 329
372 } else { 330 if (!allow_throttling_)
373 OnTimeDomainHasDelayedWork(task_queue); 331 return;
374 } 332
375 } 333 task_queue->SetTimeDomain(time_domain_.get());
334 // This blocks any tasks from |task_queue| until PumpThrottledTasks() to
335 // enforce task alignment.
336 task_queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
337
338 if (!task_queue->IsEmpty()) {
339 if (task_queue->HasPendingImmediateWork()) {
340 OnTimeDomainHasImmediateWork(task_queue);
341 } else {
342 OnTimeDomainHasDelayedWork(task_queue);
376 } 343 }
377
378 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
379 "task_queue", task_queue);
380 } 344 }
381
382 insert_result.first->second.throttling_ref_count++;
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; 352 return;
353 }
391 354
392 MaybeDeleteQueueMetadata(iter); 355 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled",
356 "task_queue", task_queue);
393 357
394 if (allow_throttling_) { 358 MaybeDeleteQueueMetadata(iter);
395 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
396 task_queue->RemoveFence();
397 task_queue->SetQueueEnabled(enabled);
398 }
399 359
400 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled", 360 if (!allow_throttling_)
401 "task_queue", task_queue); 361 return;
402 } 362
363 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
364 task_queue->RemoveFence();
403 } 365 }
404 366
405 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const { 367 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const {
406 if (!allow_throttling_) 368 if (!allow_throttling_)
407 return false; 369 return false;
408 370
409 auto find_it = queue_details_.find(task_queue); 371 auto find_it = queue_details_.find(task_queue);
410 if (find_it == queue_details_.end()) 372 if (find_it == queue_details_.end())
411 return false; 373 return false;
412 return find_it->second.throttling_ref_count > 0; 374 return find_it->second.throttling_ref_count > 0;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 417
456 void TaskQueueThrottler::PumpThrottledTasks() { 418 void TaskQueueThrottler::PumpThrottledTasks() {
457 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks"); 419 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks");
458 pending_pump_throttled_tasks_runtime_.reset(); 420 pending_pump_throttled_tasks_runtime_.reset();
459 421
460 LazyNow lazy_now(tick_clock_); 422 LazyNow lazy_now(tick_clock_);
461 base::Optional<base::TimeTicks> next_scheduled_delayed_task; 423 base::Optional<base::TimeTicks> next_scheduled_delayed_task;
462 424
463 for (const TaskQueueMap::value_type& map_entry : queue_details_) { 425 for (const TaskQueueMap::value_type& map_entry : queue_details_) {
464 TaskQueue* task_queue = map_entry.first; 426 TaskQueue* task_queue = map_entry.first;
465 if (!map_entry.second.enabled || task_queue->IsEmpty() || 427 if (task_queue->IsEmpty() || !IsThrottled(task_queue))
466 !IsThrottled(task_queue))
467 continue; 428 continue;
468 429
469 // Don't enable queues whose budget pool doesn't allow them to run now. 430 // Don't enable queues whose budget pool doesn't allow them to run now.
470 base::TimeTicks next_allowed_run_time = 431 base::TimeTicks next_allowed_run_time =
471 GetNextAllowedRunTime(lazy_now.Now(), task_queue); 432 GetNextAllowedRunTime(lazy_now.Now(), task_queue);
472 base::Optional<base::TimeTicks> next_desired_run_time = 433 base::Optional<base::TimeTicks> next_desired_run_time =
473 NextTaskRunTime(&lazy_now, task_queue); 434 NextTaskRunTime(&lazy_now, task_queue);
474 435
475 if (next_desired_run_time && 436 if (next_desired_run_time &&
476 next_allowed_run_time > next_desired_run_time.value()) { 437 next_allowed_run_time > next_desired_run_time.value()) {
477 TRACE_EVENT1( 438 TRACE_EVENT1(
478 tracing_category_, 439 tracing_category_,
479 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled", 440 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
480 "throttle_time_in_seconds", 441 "throttle_time_in_seconds",
481 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF()); 442 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF());
482 443
483 // Schedule a pump for queue which was disabled because of time budget. 444 // Schedule a pump for queue which was disabled because of time budget.
484 next_scheduled_delayed_task = 445 next_scheduled_delayed_task =
485 Min(next_scheduled_delayed_task, next_allowed_run_time); 446 Min(next_scheduled_delayed_task, next_allowed_run_time);
486 447
487 continue; 448 continue;
488 } 449 }
489 450
490 next_scheduled_delayed_task = 451 next_scheduled_delayed_task =
491 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp()); 452 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp());
492 453
493 if (next_allowed_run_time > lazy_now.Now()) 454 if (next_allowed_run_time > lazy_now.Now())
494 continue; 455 continue;
495 456
496 task_queue->SetQueueEnabled(true); 457 // Remove previous fence and install a new one, allowing all tasks posted
497 task_queue->InsertFence(); 458 // on |task_queue| up until this point to run and block all further tasks.
459 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
498 } 460 }
499 461
500 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is 462 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is
501 // a pending delayed task or a throttled task ready to run. 463 // a pending delayed task or a throttled task ready to run.
502 // NOTE: posting a non-delayed task in the future will result in 464 // NOTE: posting a non-delayed task in the future will result in
503 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called. 465 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called.
504 if (next_scheduled_delayed_task) { 466 if (next_scheduled_delayed_task) {
505 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(), 467 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(),
506 *next_scheduled_delayed_task); 468 *next_scheduled_delayed_task);
507 } 469 }
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 pool->AsValueInto(state, now); 548 pool->AsValueInto(state, now);
587 } 549 }
588 state->EndDictionary(); 550 state->EndDictionary();
589 551
590 state->BeginDictionary("queue_details"); 552 state->BeginDictionary("queue_details");
591 for (const auto& map_entry : queue_details_) { 553 for (const auto& map_entry : queue_details_) {
592 state->BeginDictionaryWithCopiedName(PointerToId(map_entry.first)); 554 state->BeginDictionaryWithCopiedName(PointerToId(map_entry.first));
593 555
594 state->SetInteger("throttling_ref_count", 556 state->SetInteger("throttling_ref_count",
595 map_entry.second.throttling_ref_count); 557 map_entry.second.throttling_ref_count);
596 state->SetBoolean("enabled", map_entry.second.enabled);
597 558
598 state->EndDictionary(); 559 state->EndDictionary();
599 } 560 }
600 state->EndDictionary(); 561 state->EndDictionary();
601 } 562 }
602 563
603 TaskQueueThrottler::TimeBudgetPool* 564 TaskQueueThrottler::TimeBudgetPool*
604 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) { 565 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) {
605 auto find_it = queue_details_.find(queue); 566 auto find_it = queue_details_.find(queue);
606 if (find_it == queue_details_.end()) 567 if (find_it == queue_details_.end())
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
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->SetTimeDomain(renderer_scheduler_->GetActiveTimeDomain()); 611 queue->SetTimeDomain(renderer_scheduler_->GetActiveTimeDomain());
651 612
652 queue->RemoveFence(); 613 queue->RemoveFence();
653 queue->SetQueueEnabled(map_entry.second.enabled);
654 } 614 }
655 615
656 pump_throttled_tasks_closure_.Cancel(); 616 pump_throttled_tasks_closure_.Cancel();
657 pending_pump_throttled_tasks_runtime_ = base::nullopt; 617 pending_pump_throttled_tasks_runtime_ = base::nullopt;
658 618
659 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_DisableThrottling"); 619 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_DisableThrottling");
660 } 620 }
661 621
662 void TaskQueueThrottler::EnableThrottling() { 622 void TaskQueueThrottler::EnableThrottling() {
663 if (allow_throttling_) 623 if (allow_throttling_)
664 return; 624 return;
665 625
666 allow_throttling_ = true; 626 allow_throttling_ = true;
667 627
668 LazyNow lazy_now(tick_clock_); 628 LazyNow lazy_now(tick_clock_);
669 629
670 for (const auto& map_entry : queue_details_) { 630 for (const auto& map_entry : queue_details_) {
671 if (map_entry.second.throttling_ref_count == 0) 631 if (map_entry.second.throttling_ref_count == 0)
672 continue; 632 continue;
673 633
674 TaskQueue* queue = map_entry.first; 634 TaskQueue* queue = map_entry.first;
675 635
676 queue->SetQueueEnabled(false); 636 // Throttling is enabled and task queue should be blocked immediately
637 // to enforce task alignment.
638 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
677 queue->SetTimeDomain(time_domain_.get()); 639 queue->SetTimeDomain(time_domain_.get());
678 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue, 640 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue,
679 GetNextAllowedRunTime(lazy_now.Now(), queue)); 641 GetNextAllowedRunTime(lazy_now.Now(), queue));
680 } 642 }
681 643
682 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling"); 644 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling");
683 } 645 }
684 646
685 } // namespace scheduler 647 } // namespace scheduler
686 } // namespace blink 648 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698