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

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

Issue 2778123003: [scheduler] Add WakeupBudgetPool. (Closed)
Patch Set: Rebased & addressed comments from skyostil@ and alexclarke@ Created 3 years, 7 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 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 if (!task_queue->IsEmpty()) { 135 if (!task_queue->IsEmpty()) {
136 LazyNow lazy_now(tick_clock_); 136 LazyNow lazy_now(tick_clock_);
137 OnQueueNextWakeUpChanged(task_queue, 137 OnQueueNextWakeUpChanged(task_queue,
138 NextTaskRunTime(&lazy_now, task_queue).value()); 138 NextTaskRunTime(&lazy_now, task_queue).value());
139 } 139 }
140 } 140 }
141 141
142 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) { 142 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
143 TaskQueueMap::iterator iter = queue_details_.find(task_queue); 143 TaskQueueMap::iterator iter = queue_details_.find(task_queue);
144 144
145 if (iter == queue_details_.end() || 145 if (iter == queue_details_.end())
146 --iter->second.throttling_ref_count != 0) {
147 return; 146 return;
148 } 147 if (iter->second.throttling_ref_count == 0)
148 return;
149 if (--iter->second.throttling_ref_count != 0)
150 return;
149 151
150 TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueUnthrottled", 152 TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueUnthrottled",
151 "task_queue", task_queue); 153 "task_queue", task_queue);
152 154
153 task_queue->SetObserver(nullptr); 155 task_queue->SetObserver(nullptr);
154 156
155 MaybeDeleteQueueMetadata(iter); 157 MaybeDeleteQueueMetadata(iter);
156 158
157 if (!allow_throttling_) 159 if (!allow_throttling_)
158 return; 160 return;
(...skipping 10 matching lines...) Expand all
169 if (find_it == queue_details_.end()) 171 if (find_it == queue_details_.end())
170 return false; 172 return false;
171 return find_it->second.throttling_ref_count > 0; 173 return find_it->second.throttling_ref_count > 0;
172 } 174 }
173 175
174 void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) { 176 void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) {
175 auto find_it = queue_details_.find(task_queue); 177 auto find_it = queue_details_.find(task_queue);
176 if (find_it == queue_details_.end()) 178 if (find_it == queue_details_.end())
177 return; 179 return;
178 180
179 LazyNow lazy_now(tick_clock_);
180 std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools; 181 std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools;
181 for (BudgetPool* budget_pool : budget_pools) { 182 for (BudgetPool* budget_pool : budget_pools) {
182 budget_pool->RemoveQueue(lazy_now.Now(), task_queue); 183 budget_pool->UnregisterQueue(task_queue);
183 } 184 }
184 185
185 // Iterator may have been deleted by BudgetPool::RemoveQueue, so don't 186 // Iterator may have been deleted by BudgetPool::RemoveQueue, so don't
186 // use it here. 187 // use it here.
187 queue_details_.erase(task_queue); 188 queue_details_.erase(task_queue);
188 189
189 // NOTE: Observer is automatically unregistered when unregistering task queue. 190 // NOTE: Observer is automatically unregistered when unregistering task queue.
190 } 191 }
191 192
192 void TaskQueueThrottler::OnQueueNextWakeUpChanged( 193 void TaskQueueThrottler::OnQueueNextWakeUpChanged(
193 TaskQueue* queue, 194 TaskQueue* queue,
194 base::TimeTicks next_wake_up) { 195 base::TimeTicks next_wake_up) {
195 if (!control_task_queue_->RunsTasksOnCurrentThread()) { 196 if (!control_task_queue_->RunsTasksOnCurrentThread()) {
196 control_task_queue_->PostTask( 197 control_task_queue_->PostTask(
197 FROM_HERE, 198 FROM_HERE,
198 base::Bind(forward_immediate_work_callback_, queue, next_wake_up)); 199 base::Bind(forward_immediate_work_callback_, queue, next_wake_up));
199 return; 200 return;
200 } 201 }
201 202
202 TRACE_EVENT0("renderer.scheduler", 203 TRACE_EVENT0("renderer.scheduler",
203 "TaskQueueThrottler::OnQueueNextWakeUpChanged"); 204 "TaskQueueThrottler::OnQueueNextWakeUpChanged");
204 205
205 // We don't expect this to get called for disabled queues, but we can't DCHECK 206 // We don't expect this to get called for disabled queues, but we can't DCHECK
206 // because of the above thread hop. Just bail out if the queue is disabled. 207 // because of the above thread hop. Just bail out if the queue is disabled.
207 if (!queue->IsQueueEnabled()) 208 if (!queue->IsQueueEnabled())
208 return; 209 return;
209 210
210 base::TimeTicks now = tick_clock_->NowTicks(); 211 base::TimeTicks now = tick_clock_->NowTicks();
212
213 auto find_it = queue_details_.find(queue);
214 if (find_it == queue_details_.end())
215 return;
216
217 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
218 budget_pool->OnQueueNextWakeUpChanged(queue, now, next_wake_up);
219 }
220
221 // TODO(altimin): This probably can be removed —- budget pools should
222 // schedule this.
223 base::TimeTicks next_allowed_run_time =
224 GetNextAllowedRunTime(queue, next_wake_up);
211 MaybeSchedulePumpThrottledTasks( 225 MaybeSchedulePumpThrottledTasks(
212 FROM_HERE, now, 226 FROM_HERE, now, std::max(next_wake_up, next_allowed_run_time));
213 std::max(GetNextAllowedRunTime(now, queue), next_wake_up));
214 } 227 }
215 228
216 void TaskQueueThrottler::PumpThrottledTasks() { 229 void TaskQueueThrottler::PumpThrottledTasks() {
217 TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler::PumpThrottledTasks"); 230 TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler::PumpThrottledTasks");
218 pending_pump_throttled_tasks_runtime_.reset(); 231 pending_pump_throttled_tasks_runtime_.reset();
219 232
220 LazyNow lazy_now(tick_clock_); 233 LazyNow lazy_now(tick_clock_);
221 base::Optional<base::TimeTicks> next_scheduled_delayed_task; 234 base::Optional<base::TimeTicks> next_scheduled_delayed_task;
222 235
236 for (const auto& pair : budget_pools_)
237 pair.first->OnWakeUp(lazy_now.Now());
238
223 for (const TaskQueueMap::value_type& map_entry : queue_details_) { 239 for (const TaskQueueMap::value_type& map_entry : queue_details_) {
224 TaskQueue* task_queue = map_entry.first; 240 TaskQueue* task_queue = map_entry.first;
225 if (task_queue->IsEmpty() || !IsThrottled(task_queue)) 241 UpdateQueueThrottlingStateInternal(lazy_now.Now(), task_queue, true);
226 continue;
227
228 // Don't enable queues whose budget pool doesn't allow them to run now.
229 base::TimeTicks next_allowed_run_time =
230 GetNextAllowedRunTime(lazy_now.Now(), task_queue);
231 base::Optional<base::TimeTicks> next_desired_run_time =
232 NextTaskRunTime(&lazy_now, task_queue);
233
234 if (next_desired_run_time &&
235 next_allowed_run_time > next_desired_run_time.value()) {
236 TRACE_EVENT1(
237 "renderer.scheduler",
238 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
239 "throttle_time_in_seconds",
240 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF());
241
242 // Schedule a pump for queue which was disabled because of time budget.
243 next_scheduled_delayed_task =
244 Min(next_scheduled_delayed_task, next_allowed_run_time);
245
246 continue;
247 }
248
249 next_scheduled_delayed_task =
250 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp());
251
252 if (next_allowed_run_time > lazy_now.Now())
253 continue;
254
255 // Remove previous fence and install a new one, allowing all tasks posted
256 // on |task_queue| up until this point to run and block all further tasks.
257 task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
258 }
259
260 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is
261 // a pending delayed task or a throttled task ready to run.
262 // NOTE: posting a non-delayed task in the future will result in
263 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called.
264 if (next_scheduled_delayed_task) {
265 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(),
266 *next_scheduled_delayed_task);
267 } 242 }
268 } 243 }
269 244
270 /* static */ 245 /* static */
271 base::TimeTicks TaskQueueThrottler::AlignedThrottledRunTime( 246 base::TimeTicks TaskQueueThrottler::AlignedThrottledRunTime(
272 base::TimeTicks unthrottled_runtime) { 247 base::TimeTicks unthrottled_runtime) {
273 const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1); 248 const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1);
274 return unthrottled_runtime + one_second - 249 return unthrottled_runtime + one_second -
275 ((unthrottled_runtime - base::TimeTicks()) % one_second); 250 ((unthrottled_runtime - base::TimeTicks()) % one_second);
276 } 251 }
(...skipping 29 matching lines...) Expand all
306 } 281 }
307 282
308 CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool( 283 CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool(
309 const char* name) { 284 const char* name) {
310 CPUTimeBudgetPool* time_budget_pool = 285 CPUTimeBudgetPool* time_budget_pool =
311 new CPUTimeBudgetPool(name, this, tick_clock_->NowTicks()); 286 new CPUTimeBudgetPool(name, this, tick_clock_->NowTicks());
312 budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); 287 budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool);
313 return time_budget_pool; 288 return time_budget_pool;
314 } 289 }
315 290
291 WakeUpBudgetPool* TaskQueueThrottler::CreateWakeUpBudgetPool(const char* name) {
292 WakeUpBudgetPool* wake_up_budget_pool =
293 new WakeUpBudgetPool(name, this, tick_clock_->NowTicks());
294 budget_pools_[wake_up_budget_pool] = base::WrapUnique(wake_up_budget_pool);
295 return wake_up_budget_pool;
296 }
297
316 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, 298 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
317 base::TimeTicks start_time, 299 base::TimeTicks start_time,
318 base::TimeTicks end_time) { 300 base::TimeTicks end_time) {
319 if (!IsThrottled(task_queue)) 301 if (!IsThrottled(task_queue))
320 return; 302 return;
321 303
322 auto find_it = queue_details_.find(task_queue); 304 auto find_it = queue_details_.find(task_queue);
323 if (find_it == queue_details_.end()) 305 if (find_it == queue_details_.end())
324 return; 306 return;
325 307
326 for (BudgetPool* budget_pool : find_it->second.budget_pools) { 308 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
327 budget_pool->RecordTaskRunTime(start_time, end_time); 309 budget_pool->RecordTaskRunTime(task_queue, start_time, end_time);
328 if (!budget_pool->HasEnoughBudgetToRun(end_time))
329 budget_pool->BlockThrottledQueues(end_time);
330 } 310 }
331 } 311 }
332 312
333 void TaskQueueThrottler::BlockQueue(base::TimeTicks now, TaskQueue* queue) { 313 void TaskQueueThrottler::UpdateQueueThrottlingState(base::TimeTicks now,
334 if (!IsThrottled(queue)) 314 TaskQueue* queue) {
315 UpdateQueueThrottlingStateInternal(now, queue, false);
316 }
317
318 void TaskQueueThrottler::UpdateQueueThrottlingStateInternal(base::TimeTicks now,
319 TaskQueue* queue,
320 bool is_wake_up) {
321 if (!queue->IsQueueEnabled() || !IsThrottled(queue)) {
335 return; 322 return;
323 }
336 324
337 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); 325 LazyNow lazy_now(now);
338 SchedulePumpQueue(FROM_HERE, now, queue); 326
327 base::Optional<base::TimeTicks> next_desired_run_time =
328 NextTaskRunTime(&lazy_now, queue);
329
330 if (!next_desired_run_time) {
331 // This queue is empty. Given that new task can arrive at any moment,
332 // block the queue completely and update the state upon the notification
333 // about a new task.
334 queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
335 return;
336 }
337
338 if (CanRunTasksAt(queue, now, false) &&
339 CanRunTasksAt(queue, next_desired_run_time.value(), false)) {
340 // We can run up until the next task uninterrupted. Remove the fence
Sami 2017/05/03 16:53:05 Wondering about the wording here ("uninterrupted")
altimin 2017/05/04 10:50:06 Made the comment more explicit.
341 // to allow new tasks to run immediately.
342 queue->RemoveFence();
343
344 // TaskQueueThrottler does not schedule wake-ups implicitly, we need
345 // to be explicit.
346 if (next_desired_run_time.value() != now) {
347 time_domain_->SetNextTaskRunTime(next_desired_run_time.value());
348 }
349 return;
350 }
351
352 if (CanRunTasksAt(queue, now, is_wake_up)) {
353 // We can run task now, but we can't run until the next scheduled task.
354 // Insert a fresh fence to unblock queue and schedule a pump for the
355 // next wake-up.
356 queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
357
358 base::Optional<base::TimeTicks> next_wake_up =
359 queue->GetNextScheduledWakeUp();
360 if (next_wake_up) {
361 MaybeSchedulePumpThrottledTasks(
362 FROM_HERE, now, GetNextAllowedRunTime(queue, next_wake_up.value()));
363 }
364 return;
365 }
366
367 base::TimeTicks next_run_time =
368 GetNextAllowedRunTime(queue, next_desired_run_time.value());
369
370 // Insert a fence of an approriate type.
371 base::Optional<QueueBlockType> block_type = GetQueueBlockType(now, queue);
372 DCHECK(block_type);
373
374 switch (block_type.value()) {
375 case QueueBlockType::kAllTasks:
376 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
377
378 {
379 // Braces limit the scope for a declared variable. Does not compile
380 // otherwise.
381 TRACE_EVENT1(
382 "renderer.scheduler",
383 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
384 "throttle_time_in_seconds",
385 (next_run_time - next_desired_run_time.value()).InSecondsF());
386 }
387 break;
388 case QueueBlockType::kNewTasksOnly:
389 if (!queue->HasFence()) {
390 // Insert a new non-fully blocking fence only when there is no fence
391 // already in order avoid undesired unblocking of old tasks.
392 queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
393 }
394 break;
395 }
396
397 // Schedule a pump.
398 MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_run_time);
399 }
400
401 base::Optional<QueueBlockType> TaskQueueThrottler::GetQueueBlockType(
402 base::TimeTicks now,
403 TaskQueue* queue) {
404 auto find_it = queue_details_.find(queue);
405 if (find_it == queue_details_.end())
406 return base::nullopt;
407
408 bool has_new_tasks_only_block = false;
409
410 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
411 if (!budget_pool->CanRunTasksAt(now, false)) {
412 if (budget_pool->GetBlockType() == QueueBlockType::kAllTasks)
413 return QueueBlockType::kAllTasks;
414 DCHECK_EQ(budget_pool->GetBlockType(), QueueBlockType::kNewTasksOnly);
415 has_new_tasks_only_block = true;
416 }
417 }
418
419 if (has_new_tasks_only_block)
420 return QueueBlockType::kNewTasksOnly;
421 return base::nullopt;
339 } 422 }
340 423
341 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, 424 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state,
342 base::TimeTicks now) const { 425 base::TimeTicks now) const {
343 if (pending_pump_throttled_tasks_runtime_) { 426 if (pending_pump_throttled_tasks_runtime_) {
344 state->SetDouble( 427 state->SetDouble(
345 "next_throttled_tasks_pump_in_seconds", 428 "next_throttled_tasks_pump_in_seconds",
346 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); 429 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF());
347 } 430 }
348 431
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
389 472
390 find_it->second.budget_pools.erase(budget_pool); 473 find_it->second.budget_pools.erase(budget_pool);
391 474
392 MaybeDeleteQueueMetadata(find_it); 475 MaybeDeleteQueueMetadata(find_it);
393 } 476 }
394 477
395 void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) { 478 void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) {
396 budget_pools_.erase(budget_pool); 479 budget_pools_.erase(budget_pool);
397 } 480 }
398 481
399 void TaskQueueThrottler::UnblockQueue(base::TimeTicks now, TaskQueue* queue) { 482 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(
400 SchedulePumpQueue(FROM_HERE, now, queue); 483 TaskQueue* queue,
401 } 484 base::TimeTicks desired_run_time) {
402 485 base::TimeTicks next_run_time = desired_run_time;
403 void TaskQueueThrottler::SchedulePumpQueue(
404 const tracked_objects::Location& from_here,
405 base::TimeTicks now,
406 TaskQueue* queue) {
407 if (!IsThrottled(queue))
408 return;
409
410 LazyNow lazy_now(now);
411 base::Optional<base::TimeTicks> next_desired_run_time =
412 NextTaskRunTime(&lazy_now, queue);
413 if (!next_desired_run_time)
414 return;
415
416 base::Optional<base::TimeTicks> next_run_time =
417 Max(next_desired_run_time, GetNextAllowedRunTime(now, queue));
418
419 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value());
420 }
421
422 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now,
423 TaskQueue* queue) {
424 base::TimeTicks next_run_time = now;
425 486
426 auto find_it = queue_details_.find(queue); 487 auto find_it = queue_details_.find(queue);
427 if (find_it == queue_details_.end()) 488 if (find_it == queue_details_.end())
428 return next_run_time; 489 return next_run_time;
429 490
430 for (BudgetPool* budget_pool : find_it->second.budget_pools) { 491 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
431 next_run_time = 492 next_run_time = std::max(
432 std::max(next_run_time, budget_pool->GetNextAllowedRunTime()); 493 next_run_time, budget_pool->GetNextAllowedRunTime(desired_run_time));
433 } 494 }
434 495
435 return next_run_time; 496 return next_run_time;
436 } 497 }
437 498
499 bool TaskQueueThrottler::CanRunTasksAt(TaskQueue* queue,
500 base::TimeTicks moment,
501 bool is_wake_up) {
502 auto find_it = queue_details_.find(queue);
503 if (find_it == queue_details_.end())
504 return true;
505
506 for (BudgetPool* budget_pool : find_it->second.budget_pools) {
507 if (!budget_pool->CanRunTasksAt(moment, is_wake_up))
508 return false;
509 }
510
511 return true;
512 }
513
438 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { 514 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) {
439 if (it->second.throttling_ref_count == 0 && it->second.budget_pools.empty()) 515 if (it->second.throttling_ref_count == 0 && it->second.budget_pools.empty())
440 queue_details_.erase(it); 516 queue_details_.erase(it);
441 } 517 }
442 518
443 void TaskQueueThrottler::DisableThrottling() { 519 void TaskQueueThrottler::DisableThrottling() {
444 if (!allow_throttling_) 520 if (!allow_throttling_)
445 return; 521 return;
446 522
447 allow_throttling_ = false; 523 allow_throttling_ = false;
(...skipping 26 matching lines...) Expand all
474 for (const auto& map_entry : queue_details_) { 550 for (const auto& map_entry : queue_details_) {
475 if (map_entry.second.throttling_ref_count == 0) 551 if (map_entry.second.throttling_ref_count == 0)
476 continue; 552 continue;
477 553
478 TaskQueue* queue = map_entry.first; 554 TaskQueue* queue = map_entry.first;
479 555
480 // Throttling is enabled and task queue should be blocked immediately 556 // Throttling is enabled and task queue should be blocked immediately
481 // to enforce task alignment. 557 // to enforce task alignment.
482 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); 558 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
483 queue->SetTimeDomain(time_domain_.get()); 559 queue->SetTimeDomain(time_domain_.get());
484 SchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue); 560 UpdateQueueThrottlingState(lazy_now.Now(), queue);
485 } 561 }
486 562
487 TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler_EnableThrottling"); 563 TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler_EnableThrottling");
488 } 564 }
489 565
490 } // namespace scheduler 566 } // namespace scheduler
491 } // namespace blink 567 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698