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

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

Issue 2538833005: [scheduler] Add options to TaskQueue::InsertFence (Closed)
Patch Set: 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 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698