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

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

Issue 2778123003: [scheduler] Add WakeupBudgetPool. (Closed)
Patch Set: First meaningful version Created 3 years, 8 months 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 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 if (!task_queue->IsEmpty()) { 137 if (!task_queue->IsEmpty()) {
138 LazyNow lazy_now(tick_clock_); 138 LazyNow lazy_now(tick_clock_);
139 OnQueueNextWakeUpChanged(task_queue, 139 OnQueueNextWakeUpChanged(task_queue,
140 NextTaskRunTime(&lazy_now, task_queue).value()); 140 NextTaskRunTime(&lazy_now, task_queue).value());
141 } 141 }
142 } 142 }
143 143
144 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) { 144 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
145 TaskQueueMap::iterator iter = queue_details_.find(task_queue); 145 TaskQueueMap::iterator iter = queue_details_.find(task_queue);
146 146
147 if (iter == queue_details_.end() || 147 if (iter == queue_details_.end())
148 --iter->second.throttling_ref_count != 0) {
149 return; 148 return;
150 } 149 if (iter->second.throttling_ref_count == 0)
alex clarke (OOO till 29th) 2017/04/21 09:14:19 That looks really weird. Is this papering over a
altimin 2017/04/25 13:22:35 There is a special test ensuring that extra Decrea
150 return;
151 if (--iter->second.throttling_ref_count != 0)
152 return;
151 153
152 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled", 154 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled",
153 "task_queue", task_queue); 155 "task_queue", task_queue);
154 156
155 task_queue->SetObserver(nullptr); 157 task_queue->SetObserver(nullptr);
156 158
157 MaybeDeleteQueueMetadata(iter); 159 MaybeDeleteQueueMetadata(iter);
158 160
159 if (!allow_throttling_) 161 if (!allow_throttling_)
160 return; 162 return;
(...skipping 10 matching lines...) Expand all
171 if (find_it == queue_details_.end()) 173 if (find_it == queue_details_.end())
172 return false; 174 return false;
173 return find_it->second.throttling_ref_count > 0; 175 return find_it->second.throttling_ref_count > 0;
174 } 176 }
175 177
176 void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) { 178 void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) {
177 auto find_it = queue_details_.find(task_queue); 179 auto find_it = queue_details_.find(task_queue);
178 if (find_it == queue_details_.end()) 180 if (find_it == queue_details_.end())
179 return; 181 return;
180 182
181 LazyNow lazy_now(tick_clock_);
182 std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools; 183 std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools;
183 for (BudgetPool* budget_pool : budget_pools) { 184 for (BudgetPool* budget_pool : budget_pools) {
184 budget_pool->RemoveQueue(lazy_now.Now(), task_queue); 185 budget_pool->UnregisterQueue(task_queue);
185 } 186 }
186 187
187 // Iterator may have been deleted by BudgetPool::RemoveQueue, so don't 188 // Iterator may have been deleted by BudgetPool::RemoveQueue, so don't
188 // use it here. 189 // use it here.
189 queue_details_.erase(task_queue); 190 queue_details_.erase(task_queue);
190 191
191 // NOTE: Observer is automatically unregistered when unregistering task queue. 192 // NOTE: Observer is automatically unregistered when unregistering task queue.
192 } 193 }
193 194
194 void TaskQueueThrottler::OnQueueNextWakeUpChanged( 195 void TaskQueueThrottler::OnQueueNextWakeUpChanged(
195 TaskQueue* queue, 196 TaskQueue* queue,
196 base::TimeTicks next_wake_up) { 197 base::TimeTicks next_wake_up) {
197 if (!task_runner_->RunsTasksOnCurrentThread()) { 198 if (!task_runner_->RunsTasksOnCurrentThread()) {
198 task_runner_->PostTask( 199 task_runner_->PostTask(
199 FROM_HERE, 200 FROM_HERE,
200 base::Bind(forward_immediate_work_callback_, queue, next_wake_up)); 201 base::Bind(forward_immediate_work_callback_, queue, next_wake_up));
201 return; 202 return;
202 } 203 }
203 204
204 TRACE_EVENT0(tracing_category_, 205 TRACE_EVENT0(tracing_category_,
205 "TaskQueueThrottler::OnQueueNextWakeUpChanged"); 206 "TaskQueueThrottler::OnQueueNextWakeUpChanged");
206 207
207 // We don't expect this to get called for disabled queues, but we can't DCHECK 208 // We don't expect this to get called for disabled queues, but we can't DCHECK
208 // because of the above thread hop. Just bail out if the queue is disabled. 209 // because of the above thread hop. Just bail out if the queue is disabled.
209 if (!queue->IsQueueEnabled()) 210 if (!queue->IsQueueEnabled())
210 return; 211 return;
211 212
212 base::TimeTicks now = tick_clock_->NowTicks(); 213 base::TimeTicks now = tick_clock_->NowTicks();
214
215 auto find_it = queue_details_.find(queue);
alex clarke (OOO till 29th) 2017/04/21 09:14:19 I don't remember off hand if return value optimiza
altimin 2017/04/25 13:22:35 auto find_it is a common pattern here. I don't wan
216 if (find_it == queue_details_.end())
217 return;
218
219 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
220 budget_pool->OnTaskQueueHasWork(queue, now, next_wake_up);
221 }
222
223 base::TimeTicks next_allowed_run_time =
224 GetNextAllowedRunTime(queue, now, next_wake_up);
225 // TODO(altimin): Remove after moving to budget pools completely.
213 MaybeSchedulePumpThrottledTasks( 226 MaybeSchedulePumpThrottledTasks(
214 FROM_HERE, now, 227 FROM_HERE, now, std::max(next_wake_up, next_allowed_run_time));
215 std::max(GetNextAllowedRunTime(now, queue), next_wake_up));
216 } 228 }
217 229
218 void TaskQueueThrottler::PumpThrottledTasks() { 230 void TaskQueueThrottler::PumpThrottledTasks() {
219 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks"); 231 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks");
220 pending_pump_throttled_tasks_runtime_.reset(); 232 pending_pump_throttled_tasks_runtime_.reset();
221 233
222 LazyNow lazy_now(tick_clock_); 234 LazyNow lazy_now(tick_clock_);
223 base::Optional<base::TimeTicks> next_scheduled_delayed_task; 235 base::Optional<base::TimeTicks> next_scheduled_delayed_task;
224 236
237 for (const auto& it : budget_pools_)
alex clarke (OOO till 29th) 2017/04/21 09:14:19 uber nit: s/it/pair ;)
altimin 2017/04/25 13:22:35 Done.
238 it.first->OnWakeup(lazy_now.Now());
239
225 for (const TaskQueueMap::value_type& map_entry : queue_details_) { 240 for (const TaskQueueMap::value_type& map_entry : queue_details_) {
226 TaskQueue* task_queue = map_entry.first; 241 TaskQueue* task_queue = map_entry.first;
227 if (task_queue->IsEmpty() || !IsThrottled(task_queue)) 242 if (task_queue->IsEmpty() || !task_queue->IsQueueEnabled() ||
243 !IsThrottled(task_queue)) {
228 continue; 244 continue;
245 }
229 246
230 // Don't enable queues whose budget pool doesn't allow them to run now.
231 base::TimeTicks next_allowed_run_time =
232 GetNextAllowedRunTime(lazy_now.Now(), task_queue);
233 base::Optional<base::TimeTicks> next_desired_run_time = 247 base::Optional<base::TimeTicks> next_desired_run_time =
234 NextTaskRunTime(&lazy_now, task_queue); 248 NextTaskRunTime(&lazy_now, task_queue);
235 249
236 if (next_desired_run_time && 250 if (!next_desired_run_time) {
237 next_allowed_run_time > next_desired_run_time.value()) { 251 // This task queue does not any tasks.
alex clarke (OOO till 29th) 2017/04/21 09:14:19 Looks like you're missing a word in the comment.
altimin 2017/04/25 13:22:35 Done.
252 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
alex clarke (OOO till 29th) 2017/04/21 09:14:19 Is it possible to get here? The queue has to be e
altimin 2017/04/25 13:22:35 Done.
253 continue;
254 }
255
256 base::TimeTicks next_run_time = GetNextAllowedRunTime(
257 task_queue, lazy_now.Now(), next_desired_run_time.value());
258
259 if (next_run_time > lazy_now.Now()) {
238 TRACE_EVENT1( 260 TRACE_EVENT1(
239 tracing_category_, 261 tracing_category_,
240 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled", 262 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
241 "throttle_time_in_seconds", 263 "throttle_time_in_seconds",
242 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF()); 264 (next_run_time - next_desired_run_time.value()).InSecondsF());
243 265
244 // Schedule a pump for queue which was disabled because of time budget.
245 next_scheduled_delayed_task = 266 next_scheduled_delayed_task =
246 Min(next_scheduled_delayed_task, next_allowed_run_time); 267 Min(next_scheduled_delayed_task, next_run_time);
247 268
248 continue; 269 continue;
249 } 270 }
250 271
251 next_scheduled_delayed_task = 272 base::Optional<base::TimeTicks> next_wake_up =
252 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp()); 273 task_queue->GetNextScheduledWakeUp();
253 274
254 if (next_allowed_run_time > lazy_now.Now()) 275 if (next_wake_up) {
276 next_scheduled_delayed_task =
277 Min(next_scheduled_delayed_task,
278 GetNextAllowedRunTime(task_queue, lazy_now.Now(),
279 next_wake_up.value()));
280 }
281
282 if (CanRunTasksUntil(task_queue, lazy_now.Now(),
283 next_desired_run_time.value())) {
284 // Remove fence if we can new tasks until next wakeup.
285 task_queue->RemoveFence();
255 continue; 286 continue;
287 }
256 288
257 // Remove previous fence and install a new one, allowing all tasks posted 289 // Remove previous fence and install a new one, allowing all tasks posted
258 // on |task_queue| up until this point to run and block all further tasks. 290 // on |task_queue| up until this point to run and block all further tasks.
259 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW); 291 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
260 } 292 }
261 293
262 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is 294 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is
263 // a pending delayed task or a throttled task ready to run. 295 // a pending delayed task or a throttled task ready to run.
264 // NOTE: posting a non-delayed task in the future will result in 296 // NOTE: posting a non-delayed task in the future will result in
265 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called. 297 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called.
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 } 340 }
309 341
310 CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool( 342 CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool(
311 const char* name) { 343 const char* name) {
312 CPUTimeBudgetPool* time_budget_pool = 344 CPUTimeBudgetPool* time_budget_pool =
313 new CPUTimeBudgetPool(name, this, tick_clock_->NowTicks()); 345 new CPUTimeBudgetPool(name, this, tick_clock_->NowTicks());
314 budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); 346 budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool);
315 return time_budget_pool; 347 return time_budget_pool;
316 } 348 }
317 349
350 WakeupBudgetPool* TaskQueueThrottler::CreateWakeupBudgetPool(const char* name) {
351 WakeupBudgetPool* wakeup_budget_pool =
352 new WakeupBudgetPool(name, this, tick_clock_->NowTicks());
353 budget_pools_[wakeup_budget_pool] = base::WrapUnique(wakeup_budget_pool);
354 return wakeup_budget_pool;
355 }
356
318 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, 357 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
319 base::TimeTicks start_time, 358 base::TimeTicks start_time,
320 base::TimeTicks end_time) { 359 base::TimeTicks end_time) {
321 if (!IsThrottled(task_queue)) 360 if (!IsThrottled(task_queue))
322 return; 361 return;
323 362
324 auto find_it = queue_details_.find(task_queue); 363 auto find_it = queue_details_.find(task_queue);
325 if (find_it == queue_details_.end()) 364 if (find_it == queue_details_.end())
326 return; 365 return;
327 366
328 for (BudgetPool* budget_pool : find_it->second.budget_pools) { 367 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
329 budget_pool->RecordTaskRunTime(start_time, end_time); 368 budget_pool->RecordTaskRunTime(task_queue, start_time, end_time);
330 if (!budget_pool->HasEnoughBudgetToRun(end_time)) 369 if (!budget_pool->CanRunTasksAt(end_time))
331 budget_pool->BlockThrottledQueues(end_time); 370 budget_pool->BlockThrottledQueues(end_time);
332 } 371 }
333 } 372 }
334 373
335 void TaskQueueThrottler::BlockQueue(base::TimeTicks now, TaskQueue* queue) { 374 void TaskQueueThrottler::BlockQueue(QueueBlockType block_type,
375 base::TimeTicks now,
376 TaskQueue* queue) {
336 if (!IsThrottled(queue)) 377 if (!IsThrottled(queue))
337 return; 378 return;
338 379
339 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); 380 if (block_type == QueueBlockType::FULL) {
381 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
382 } else { // block_type == BlockType::NEW_TASKS_ONLY
383 if (!queue->HasFence()) {
384 queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
385 }
386 }
340 SchedulePumpQueue(FROM_HERE, now, queue); 387 SchedulePumpQueue(FROM_HERE, now, queue);
341 } 388 }
342 389
343 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, 390 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state,
344 base::TimeTicks now) const { 391 base::TimeTicks now) const {
345 if (pending_pump_throttled_tasks_runtime_) { 392 if (pending_pump_throttled_tasks_runtime_) {
346 state->SetDouble( 393 state->SetDouble(
347 "next_throttled_tasks_pump_in_seconds", 394 "next_throttled_tasks_pump_in_seconds",
348 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); 395 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF());
349 } 396 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 find_it->second.budget_pools.erase(budget_pool); 439 find_it->second.budget_pools.erase(budget_pool);
393 440
394 MaybeDeleteQueueMetadata(find_it); 441 MaybeDeleteQueueMetadata(find_it);
395 } 442 }
396 443
397 void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) { 444 void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) {
398 budget_pools_.erase(budget_pool); 445 budget_pools_.erase(budget_pool);
399 } 446 }
400 447
401 void TaskQueueThrottler::UnblockQueue(base::TimeTicks now, TaskQueue* queue) { 448 void TaskQueueThrottler::UnblockQueue(base::TimeTicks now, TaskQueue* queue) {
402 SchedulePumpQueue(FROM_HERE, now, queue); 449 if (queue->HasFence())
alex clarke (OOO till 29th) 2017/04/21 09:14:19 I hope we don't get tempted to use fences for some
altimin 2017/04/25 13:22:36 Actually, we don't need this condition here.
450 SchedulePumpQueue(FROM_HERE, now, queue);
403 } 451 }
404 452
405 void TaskQueueThrottler::SchedulePumpQueue( 453 void TaskQueueThrottler::SchedulePumpQueue(
406 const tracked_objects::Location& from_here, 454 const tracked_objects::Location& from_here,
407 base::TimeTicks now, 455 base::TimeTicks now,
408 TaskQueue* queue) { 456 TaskQueue* queue) {
409 if (!IsThrottled(queue)) 457 if (!IsThrottled(queue) || !queue->IsQueueEnabled())
410 return; 458 return;
411 459
412 LazyNow lazy_now(now); 460 LazyNow lazy_now(now);
413 base::Optional<base::TimeTicks> next_desired_run_time = 461 base::Optional<base::TimeTicks> next_desired_run_time =
414 NextTaskRunTime(&lazy_now, queue); 462 NextTaskRunTime(&lazy_now, queue);
415 if (!next_desired_run_time) 463 if (!next_desired_run_time) {
416 return; 464 return;
417 465 }
418 base::Optional<base::TimeTicks> next_run_time = 466 base::Optional<base::TimeTicks> next_run_time;
419 Max(next_desired_run_time, GetNextAllowedRunTime(now, queue)); 467 if (next_desired_run_time) {
468 next_run_time =
469 GetNextAllowedRunTime(queue, now, next_desired_run_time.value());
470 }
420 471
421 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value()); 472 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value());
422 } 473 }
423 474
424 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now, 475 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(
425 TaskQueue* queue) { 476 TaskQueue* queue,
477 base::TimeTicks now,
alex clarke (OOO till 29th) 2017/04/21 09:14:19 I'm not 100% on this, but I wonder if things would
altimin 2017/04/25 13:22:35 Agreed.
478 base::TimeTicks desired_run_time) {
426 base::TimeTicks next_run_time = now; 479 base::TimeTicks next_run_time = now;
427 480
428 auto find_it = queue_details_.find(queue); 481 auto find_it = queue_details_.find(queue);
429 if (find_it == queue_details_.end()) 482 if (find_it == queue_details_.end())
430 return next_run_time; 483 return next_run_time;
431 484
432 for (BudgetPool* budget_pool : find_it->second.budget_pools) { 485 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
433 next_run_time = 486 next_run_time = std::max(
434 std::max(next_run_time, budget_pool->GetNextAllowedRunTime()); 487 next_run_time, budget_pool->GetNextAllowedRunTime(desired_run_time));
435 } 488 }
436 489
437 return next_run_time; 490 return next_run_time;
438 } 491 }
439 492
493 bool TaskQueueThrottler::CanRunTasksAt(TaskQueue* queue,
494 base::TimeTicks moment) {
495 auto find_it = queue_details_.find(queue);
496 if (find_it == queue_details_.end())
497 return true;
498
499 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
500 if (!budget_pool->CanRunTasksAt(moment))
501 return false;
502 }
503
504 return true;
505 }
506
507 bool TaskQueueThrottler::CanRunTasksUntil(TaskQueue* queue,
508 base::TimeTicks now,
509 base::TimeTicks moment) {
510 auto find_it = queue_details_.find(queue);
511 if (find_it == queue_details_.end())
512 return true;
513
514 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
515 if (!budget_pool->CanRunTasksUntil(now, moment))
516 return false;
517 }
518
519 return true;
520 }
521
440 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { 522 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) {
441 if (it->second.throttling_ref_count == 0 && it->second.budget_pools.empty()) 523 if (it->second.throttling_ref_count == 0 && it->second.budget_pools.empty())
442 queue_details_.erase(it); 524 queue_details_.erase(it);
443 } 525 }
444 526
445 void TaskQueueThrottler::DisableThrottling() { 527 void TaskQueueThrottler::DisableThrottling() {
446 if (!allow_throttling_) 528 if (!allow_throttling_)
447 return; 529 return;
448 530
449 allow_throttling_ = false; 531 allow_throttling_ = false;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
484 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); 566 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
485 queue->SetTimeDomain(time_domain_.get()); 567 queue->SetTimeDomain(time_domain_.get());
486 SchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue); 568 SchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue);
487 } 569 }
488 570
489 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling"); 571 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling");
490 } 572 }
491 573
492 } // namespace scheduler 574 } // namespace scheduler
493 } // namespace blink 575 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698