OLD | NEW |
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" |
11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
12 #include "base/optional.h" | 12 #include "base/optional.h" |
13 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
14 #include "platform/WebFrameScheduler.h" | 14 #include "platform/WebFrameScheduler.h" |
15 #include "platform/scheduler/base/real_time_domain.h" | 15 #include "platform/scheduler/base/real_time_domain.h" |
16 #include "platform/scheduler/child/scheduler_tqm_delegate.h" | 16 #include "platform/scheduler/child/scheduler_tqm_delegate.h" |
| 17 #include "platform/scheduler/renderer/budget_pool.h" |
17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" | 18 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" |
18 #include "platform/scheduler/renderer/throttled_time_domain.h" | 19 #include "platform/scheduler/renderer/throttled_time_domain.h" |
19 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" | 20 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" |
20 | 21 |
21 namespace blink { | 22 namespace blink { |
22 namespace scheduler { | 23 namespace scheduler { |
23 | 24 |
24 namespace { | 25 namespace { |
25 | 26 |
26 base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now, | 27 base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now, |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 } | 65 } |
65 | 66 |
66 std::string PointerToId(void* pointer) { | 67 std::string PointerToId(void* pointer) { |
67 return base::StringPrintf( | 68 return base::StringPrintf( |
68 "0x%" PRIx64, | 69 "0x%" PRIx64, |
69 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer))); | 70 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer))); |
70 } | 71 } |
71 | 72 |
72 } // namespace | 73 } // namespace |
73 | 74 |
74 TaskQueueThrottler::TimeBudgetPool::TimeBudgetPool( | |
75 const char* name, | |
76 TaskQueueThrottler* task_queue_throttler, | |
77 base::TimeTicks now, | |
78 base::Optional<base::TimeDelta> max_budget_level, | |
79 base::Optional<base::TimeDelta> max_throttling_duration) | |
80 : name_(name), | |
81 task_queue_throttler_(task_queue_throttler), | |
82 max_budget_level_(max_budget_level), | |
83 max_throttling_duration_(max_throttling_duration), | |
84 last_checkpoint_(now), | |
85 cpu_percentage_(1), | |
86 is_enabled_(true) {} | |
87 | |
88 TaskQueueThrottler::TimeBudgetPool::~TimeBudgetPool() {} | |
89 | |
90 void TaskQueueThrottler::TimeBudgetPool::SetTimeBudgetRecoveryRate( | |
91 base::TimeTicks now, | |
92 double cpu_percentage) { | |
93 Advance(now); | |
94 cpu_percentage_ = cpu_percentage; | |
95 EnforceBudgetLevelRestrictions(); | |
96 } | |
97 | |
98 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, | |
99 TaskQueue* queue) { | |
100 std::pair<TaskQueueMap::iterator, bool> insert_result = | |
101 task_queue_throttler_->queue_details_.insert( | |
102 std::make_pair(queue, Metadata())); | |
103 Metadata& metadata = insert_result.first->second; | |
104 DCHECK(!metadata.time_budget_pool); | |
105 metadata.time_budget_pool = this; | |
106 | |
107 associated_task_queues_.insert(queue); | |
108 | |
109 if (!is_enabled_ || !task_queue_throttler_->IsThrottled(queue)) | |
110 return; | |
111 | |
112 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); | |
113 | |
114 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
115 GetNextAllowedRunTime()); | |
116 } | |
117 | |
118 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now, | |
119 TaskQueue* queue) { | |
120 auto find_it = task_queue_throttler_->queue_details_.find(queue); | |
121 DCHECK(find_it != task_queue_throttler_->queue_details_.end() && | |
122 find_it->second.time_budget_pool == this); | |
123 find_it->second.time_budget_pool = nullptr; | |
124 bool is_throttled = task_queue_throttler_->IsThrottled(queue); | |
125 | |
126 task_queue_throttler_->MaybeDeleteQueueMetadata(find_it); | |
127 associated_task_queues_.erase(queue); | |
128 | |
129 if (!is_enabled_ || !is_throttled) | |
130 return; | |
131 | |
132 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
133 base::nullopt); | |
134 } | |
135 | |
136 void TaskQueueThrottler::TimeBudgetPool::EnableThrottling(LazyNow* lazy_now) { | |
137 if (is_enabled_) | |
138 return; | |
139 is_enabled_ = true; | |
140 | |
141 TRACE_EVENT0(task_queue_throttler_->tracing_category_, | |
142 "TaskQueueThrottler_TimeBudgetPool_EnableThrottling"); | |
143 | |
144 BlockThrottledQueues(lazy_now->Now()); | |
145 } | |
146 | |
147 void TaskQueueThrottler::TimeBudgetPool::DisableThrottling(LazyNow* lazy_now) { | |
148 if (!is_enabled_) | |
149 return; | |
150 is_enabled_ = false; | |
151 | |
152 TRACE_EVENT0(task_queue_throttler_->tracing_category_, | |
153 "TaskQueueThrottler_TimeBudgetPool_DisableThrottling"); | |
154 | |
155 for (TaskQueue* queue : associated_task_queues_) { | |
156 if (!task_queue_throttler_->IsThrottled(queue)) | |
157 continue; | |
158 | |
159 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, lazy_now->Now(), | |
160 queue, base::nullopt); | |
161 } | |
162 | |
163 // TODO(altimin): We need to disable TimeBudgetQueues here or they will | |
164 // regenerate extra time budget when they are disabled. | |
165 } | |
166 | |
167 bool TaskQueueThrottler::TimeBudgetPool::IsThrottlingEnabled() const { | |
168 return is_enabled_; | |
169 } | |
170 | |
171 void TaskQueueThrottler::TimeBudgetPool::GrantAdditionalBudget( | |
172 base::TimeTicks now, | |
173 base::TimeDelta budget_level) { | |
174 Advance(now); | |
175 current_budget_level_ += budget_level; | |
176 EnforceBudgetLevelRestrictions(); | |
177 } | |
178 | |
179 void TaskQueueThrottler::TimeBudgetPool::SetReportingCallback( | |
180 base::Callback<void(base::TimeDelta)> reporting_callback) { | |
181 reporting_callback_ = reporting_callback; | |
182 } | |
183 | |
184 void TaskQueueThrottler::TimeBudgetPool::Close() { | |
185 DCHECK_EQ(0u, associated_task_queues_.size()); | |
186 | |
187 task_queue_throttler_->time_budget_pools_.erase(this); | |
188 } | |
189 | |
190 bool TaskQueueThrottler::TimeBudgetPool::HasEnoughBudgetToRun( | |
191 base::TimeTicks now) { | |
192 Advance(now); | |
193 return !is_enabled_ || current_budget_level_.InMicroseconds() >= 0; | |
194 } | |
195 | |
196 base::TimeTicks TaskQueueThrottler::TimeBudgetPool::GetNextAllowedRunTime() { | |
197 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | |
198 return last_checkpoint_; | |
199 } else { | |
200 // Subtract because current_budget is negative. | |
201 return last_checkpoint_ - current_budget_level_ / cpu_percentage_; | |
202 } | |
203 } | |
204 | |
205 void TaskQueueThrottler::TimeBudgetPool::RecordTaskRunTime( | |
206 base::TimeTicks start_time, | |
207 base::TimeTicks end_time) { | |
208 DCHECK_LE(start_time, end_time); | |
209 Advance(end_time); | |
210 if (is_enabled_) { | |
211 base::TimeDelta old_budget_level = current_budget_level_; | |
212 current_budget_level_ -= (end_time - start_time); | |
213 EnforceBudgetLevelRestrictions(); | |
214 | |
215 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && | |
216 current_budget_level_.InSecondsF() < 0) { | |
217 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); | |
218 } | |
219 } | |
220 } | |
221 | |
222 const char* TaskQueueThrottler::TimeBudgetPool::Name() const { | |
223 return name_; | |
224 } | |
225 | |
226 void TaskQueueThrottler::TimeBudgetPool::AsValueInto( | |
227 base::trace_event::TracedValue* state, | |
228 base::TimeTicks now) const { | |
229 state->BeginDictionary(name_); | |
230 | |
231 state->SetString("name", name_); | |
232 state->SetDouble("time_budget", cpu_percentage_); | |
233 state->SetDouble("time_budget_level_in_seconds", | |
234 current_budget_level_.InSecondsF()); | |
235 state->SetDouble("last_checkpoint_seconds_ago", | |
236 (now - last_checkpoint_).InSecondsF()); | |
237 state->SetBoolean("is_enabled", is_enabled_); | |
238 | |
239 state->BeginArray("task_queues"); | |
240 for (TaskQueue* queue : associated_task_queues_) { | |
241 state->AppendString(PointerToId(queue)); | |
242 } | |
243 state->EndArray(); | |
244 | |
245 state->EndDictionary(); | |
246 } | |
247 | |
248 void TaskQueueThrottler::TimeBudgetPool::Advance(base::TimeTicks now) { | |
249 if (now > last_checkpoint_) { | |
250 if (is_enabled_) { | |
251 current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_); | |
252 EnforceBudgetLevelRestrictions(); | |
253 } | |
254 last_checkpoint_ = now; | |
255 } | |
256 } | |
257 | |
258 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues( | |
259 base::TimeTicks now) { | |
260 for (TaskQueue* queue : associated_task_queues_) { | |
261 if (!task_queue_throttler_->IsThrottled(queue)) | |
262 continue; | |
263 | |
264 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); | |
265 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
266 base::nullopt); | |
267 } | |
268 } | |
269 | |
270 void TaskQueueThrottler::TimeBudgetPool::EnforceBudgetLevelRestrictions() { | |
271 if (max_budget_level_) { | |
272 current_budget_level_ = | |
273 std::min(current_budget_level_, max_budget_level_.value()); | |
274 } | |
275 if (max_throttling_duration_) { | |
276 // Current budget level may be negative. | |
277 current_budget_level_ = | |
278 std::max(current_budget_level_, | |
279 -max_throttling_duration_.value() * cpu_percentage_); | |
280 } | |
281 } | |
282 | |
283 TaskQueueThrottler::TaskQueueThrottler( | 75 TaskQueueThrottler::TaskQueueThrottler( |
284 RendererSchedulerImpl* renderer_scheduler, | 76 RendererSchedulerImpl* renderer_scheduler, |
285 const char* tracing_category) | 77 const char* tracing_category) |
286 : task_runner_(renderer_scheduler->ControlTaskRunner()), | 78 : task_runner_(renderer_scheduler->ControlTaskRunner()), |
287 renderer_scheduler_(renderer_scheduler), | 79 renderer_scheduler_(renderer_scheduler), |
288 tick_clock_(renderer_scheduler->tick_clock()), | 80 tick_clock_(renderer_scheduler->tick_clock()), |
289 tracing_category_(tracing_category), | 81 tracing_category_(tracing_category), |
290 time_domain_(new ThrottledTimeDomain(this, tracing_category)), | 82 time_domain_(new ThrottledTimeDomain(this, tracing_category)), |
291 allow_throttling_(true), | 83 allow_throttling_(true), |
292 weak_factory_(this) { | 84 weak_factory_(this) { |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
509 pump_throttled_tasks_closure_.Cancel(); | 301 pump_throttled_tasks_closure_.Cancel(); |
510 | 302 |
511 base::TimeDelta delay = pending_pump_throttled_tasks_runtime_.value() - now; | 303 base::TimeDelta delay = pending_pump_throttled_tasks_runtime_.value() - now; |
512 TRACE_EVENT1(tracing_category_, | 304 TRACE_EVENT1(tracing_category_, |
513 "TaskQueueThrottler::MaybeSchedulePumpThrottledTasks", | 305 "TaskQueueThrottler::MaybeSchedulePumpThrottledTasks", |
514 "delay_till_next_pump_ms", delay.InMilliseconds()); | 306 "delay_till_next_pump_ms", delay.InMilliseconds()); |
515 task_runner_->PostDelayedTask( | 307 task_runner_->PostDelayedTask( |
516 from_here, pump_throttled_tasks_closure_.callback(), delay); | 308 from_here, pump_throttled_tasks_closure_.callback(), delay); |
517 } | 309 } |
518 | 310 |
519 TaskQueueThrottler::TimeBudgetPool* TaskQueueThrottler::CreateTimeBudgetPool( | 311 CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool( |
520 const char* name, | 312 const char* name, |
521 base::Optional<base::TimeDelta> max_budget_level, | 313 base::Optional<base::TimeDelta> max_budget_level, |
522 base::Optional<base::TimeDelta> max_throttling_duration) { | 314 base::Optional<base::TimeDelta> max_throttling_duration) { |
523 TimeBudgetPool* time_budget_pool = | 315 CPUTimeBudgetPool* time_budget_pool = |
524 new TimeBudgetPool(name, this, tick_clock_->NowTicks(), max_budget_level, | 316 new CPUTimeBudgetPool(name, this, tick_clock_->NowTicks(), |
525 max_throttling_duration); | 317 max_budget_level, max_throttling_duration); |
526 time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); | 318 time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); |
527 return time_budget_pool; | 319 return time_budget_pool; |
528 } | 320 } |
529 | 321 |
530 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, | 322 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, |
531 base::TimeTicks start_time, | 323 base::TimeTicks start_time, |
532 base::TimeTicks end_time) { | 324 base::TimeTicks end_time) { |
533 if (!IsThrottled(task_queue)) | 325 if (!IsThrottled(task_queue)) |
534 return; | 326 return; |
535 | 327 |
536 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue); | 328 CPUTimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue); |
537 if (!time_budget_pool) | 329 if (!time_budget_pool) |
538 return; | 330 return; |
539 | 331 |
540 time_budget_pool->RecordTaskRunTime(start_time, end_time); | 332 time_budget_pool->RecordTaskRunTime(start_time, end_time); |
541 if (!time_budget_pool->HasEnoughBudgetToRun(end_time)) | 333 if (!time_budget_pool->HasEnoughBudgetToRun(end_time)) |
542 time_budget_pool->BlockThrottledQueues(end_time); | 334 time_budget_pool->BlockThrottledQueues(end_time); |
543 } | 335 } |
544 | 336 |
545 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, | 337 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, |
546 base::TimeTicks now) const { | 338 base::TimeTicks now) const { |
547 if (pending_pump_throttled_tasks_runtime_) { | 339 if (pending_pump_throttled_tasks_runtime_) { |
548 state->SetDouble( | 340 state->SetDouble( |
549 "next_throttled_tasks_pump_in_seconds", | 341 "next_throttled_tasks_pump_in_seconds", |
550 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); | 342 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); |
551 } | 343 } |
552 | 344 |
553 state->SetBoolean("allow_throttling", allow_throttling_); | 345 state->SetBoolean("allow_throttling", allow_throttling_); |
554 | 346 |
555 state->BeginDictionary("time_budget_pools"); | 347 state->BeginDictionary("time_budget_pools"); |
556 for (const auto& map_entry : time_budget_pools_) { | 348 for (const auto& map_entry : time_budget_pools_) { |
557 TaskQueueThrottler::TimeBudgetPool* pool = map_entry.first; | 349 BudgetPool* pool = map_entry.first; |
558 pool->AsValueInto(state, now); | 350 pool->AsValueInto(state, now); |
559 } | 351 } |
560 state->EndDictionary(); | 352 state->EndDictionary(); |
561 | 353 |
562 state->BeginDictionary("queue_details"); | 354 state->BeginDictionary("queue_details"); |
563 for (const auto& map_entry : queue_details_) { | 355 for (const auto& map_entry : queue_details_) { |
564 state->BeginDictionaryWithCopiedName(PointerToId(map_entry.first)); | 356 state->BeginDictionaryWithCopiedName(PointerToId(map_entry.first)); |
565 | 357 |
566 state->SetInteger("throttling_ref_count", | 358 state->SetInteger("throttling_ref_count", |
567 map_entry.second.throttling_ref_count); | 359 map_entry.second.throttling_ref_count); |
568 | 360 |
569 state->EndDictionary(); | 361 state->EndDictionary(); |
570 } | 362 } |
571 state->EndDictionary(); | 363 state->EndDictionary(); |
572 } | 364 } |
573 | 365 |
574 TaskQueueThrottler::TimeBudgetPool* | 366 CPUTimeBudgetPool* TaskQueueThrottler::GetTimeBudgetPoolForQueue( |
575 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) { | 367 TaskQueue* queue) { |
576 auto find_it = queue_details_.find(queue); | 368 auto find_it = queue_details_.find(queue); |
577 if (find_it == queue_details_.end()) | 369 if (find_it == queue_details_.end()) |
578 return nullptr; | 370 return nullptr; |
579 return find_it->second.time_budget_pool; | 371 return find_it->second.time_budget_pool; |
580 } | 372 } |
581 | 373 |
582 void TaskQueueThrottler::MaybeSchedulePumpQueue( | 374 void TaskQueueThrottler::MaybeSchedulePumpQueue( |
583 const tracked_objects::Location& from_here, | 375 const tracked_objects::Location& from_here, |
584 base::TimeTicks now, | 376 base::TimeTicks now, |
585 TaskQueue* queue, | 377 TaskQueue* queue, |
586 base::Optional<base::TimeTicks> next_possible_run_time) { | 378 base::Optional<base::TimeTicks> next_possible_run_time) { |
587 LazyNow lazy_now(now); | 379 LazyNow lazy_now(now); |
588 base::Optional<base::TimeTicks> next_run_time = | 380 base::Optional<base::TimeTicks> next_run_time = |
589 Max(NextTaskRunTime(&lazy_now, queue), next_possible_run_time); | 381 Max(NextTaskRunTime(&lazy_now, queue), next_possible_run_time); |
590 | 382 |
591 if (next_run_time) { | 383 if (next_run_time) { |
592 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value()); | 384 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value()); |
593 } | 385 } |
594 } | 386 } |
595 | 387 |
596 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now, | 388 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now, |
597 TaskQueue* queue) { | 389 TaskQueue* queue) { |
598 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(queue); | 390 CPUTimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(queue); |
599 if (!time_budget_pool) | 391 if (!time_budget_pool) |
600 return now; | 392 return now; |
601 return std::max(now, time_budget_pool->GetNextAllowedRunTime()); | 393 return std::max(now, time_budget_pool->GetNextAllowedRunTime()); |
602 } | 394 } |
603 | 395 |
604 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { | 396 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { |
605 if (it->second.throttling_ref_count == 0 && !it->second.time_budget_pool) | 397 if (it->second.throttling_ref_count == 0 && !it->second.time_budget_pool) |
606 queue_details_.erase(it); | 398 queue_details_.erase(it); |
607 } | 399 } |
608 | 400 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
649 queue->SetTimeDomain(time_domain_.get()); | 441 queue->SetTimeDomain(time_domain_.get()); |
650 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue, | 442 MaybeSchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue, |
651 GetNextAllowedRunTime(lazy_now.Now(), queue)); | 443 GetNextAllowedRunTime(lazy_now.Now(), queue)); |
652 } | 444 } |
653 | 445 |
654 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling"); | 446 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler_EnableThrottling"); |
655 } | 447 } |
656 | 448 |
657 } // namespace scheduler | 449 } // namespace scheduler |
658 } // namespace blink | 450 } // namespace blink |
OLD | NEW |