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

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

Issue 2258133002: [scheduler] Implement time-based cpu throttling. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 4 years, 2 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/throttling_helper.h" 5 #include "platform/scheduler/renderer/task_queue_throttler.h"
6 6
7 #include <cstdint>
8
9 #include "base/format_macros.h"
7 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/optional.h"
13 #include "base/strings/stringprintf.h"
8 #include "platform/scheduler/base/real_time_domain.h" 14 #include "platform/scheduler/base/real_time_domain.h"
9 #include "platform/scheduler/child/scheduler_tqm_delegate.h" 15 #include "platform/scheduler/child/scheduler_tqm_delegate.h"
10 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h" 16 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
11 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" 17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h"
12 #include "platform/scheduler/renderer/throttled_time_domain.h" 18 #include "platform/scheduler/renderer/throttled_time_domain.h"
13 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" 19 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h"
14 #include "public/platform/WebFrameScheduler.h" 20 #include "public/platform/WebFrameScheduler.h"
15 21
16 namespace blink { 22 namespace blink {
17 namespace scheduler { 23 namespace scheduler {
18 24
19 ThrottlingHelper::ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler, 25 namespace {
20 const char* tracing_category) 26 const int kMaxBudgetLevelInSeconds = 1;
27
28 base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now,
29 TaskQueue* queue) {
30 if (queue->HasPendingImmediateWork())
31 return lazy_now->Now();
32 return queue->GetNextScheduledWakeUp();
33 }
34
35 template <class T>
36 T Min(const base::Optional<T>& optional, const T& value) {
37 if (!optional) {
38 return value;
39 }
40 return std::min(optional.value(), value);
41 }
42
43 template <class T>
44 base::Optional<T> Min(const base::Optional<T>& a, const base::Optional<T>& b) {
45 if (!b)
46 return a;
47 if (!a)
48 return b;
49 return std::min(a.value(), b.value());
50 }
51
52 template <class T>
53 T Max(const base::Optional<T>& optional, const T& value) {
54 if (!optional)
55 return value;
56 return std::max(optional.value(), value);
57 }
58
59 template <class T>
60 base::Optional<T> Max(const base::Optional<T>& a, const base::Optional<T>& b) {
61 if (!b)
62 return a;
63 if (!a)
64 return b;
65 return std::max(a.value(), b.value());
66 }
67
68 } // namespace
69
70 TaskQueueThrottler::TimeBudgetPool::TimeBudgetPool(
71 const char* name,
72 TaskQueueThrottler* task_queue_throttler,
73 base::TimeTicks now)
74 : name_(name),
75 task_queue_throttler_(task_queue_throttler),
76 max_budget_level_(base::TimeDelta::FromSeconds(kMaxBudgetLevelInSeconds)),
77 last_checkpoint_(now),
78 cpu_percentage_(1),
79 is_enabled_(true) {}
80
81 TaskQueueThrottler::TimeBudgetPool::~TimeBudgetPool() {}
82
83 void TaskQueueThrottler::TimeBudgetPool::SetTimeBudget(base::TimeTicks now,
84 double cpu_percentage) {
85 Advance(now);
86 cpu_percentage_ = cpu_percentage;
87 }
88
89 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now,
90 TaskQueue* queue) {
91 Metadata& metadata = task_queue_throttler_->queue_details_[queue];
92 DCHECK(!metadata.time_budget_pool);
93 metadata.time_budget_pool = this;
94
95 associated_task_queues_.insert(queue);
96
97 if (!metadata.IsThrottled())
98 return;
99
100 queue->SetQueueEnabled(false);
101
102 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue,
103 GetNextAllowedRunTime());
104 }
105
106 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now,
107 TaskQueue* queue) {
108 auto find_it = task_queue_throttler_->queue_details_.find(queue);
109 DCHECK(find_it != task_queue_throttler_->queue_details_.end() &&
110 find_it->second.time_budget_pool == this);
111 find_it->second.time_budget_pool = nullptr;
112 bool is_throttled = find_it->second.IsThrottled();
113
114 task_queue_throttler_->MaybeDeleteQueueMetadata(find_it);
115 associated_task_queues_.erase(queue);
116
117 if (is_throttled)
118 return;
119
120 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue,
121 base::nullopt);
122 }
123
124 void TaskQueueThrottler::TimeBudgetPool::EnableThrottling(LazyNow* lazy_now) {
125 if (is_enabled_)
126 return;
127 is_enabled_ = true;
128
129 BlockThrottledQueues(lazy_now->Now());
130 }
131
132 void TaskQueueThrottler::TimeBudgetPool::DisableThrottling(LazyNow* lazy_now) {
133 if (!is_enabled_)
134 return;
135 is_enabled_ = false;
136
137 for (TaskQueue* queue : associated_task_queues_) {
138 if (!task_queue_throttler_->IsThrottled(queue))
139 continue;
140
141 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, lazy_now->Now(),
142 queue, base::nullopt);
143 }
144 }
145
146 bool TaskQueueThrottler::TimeBudgetPool::IsThrottlingEnabled() const {
147 return is_enabled_;
148 }
149
150 void TaskQueueThrottler::TimeBudgetPool::Close() {
151 DCHECK_EQ(0u, associated_task_queues_.size());
152
153 task_queue_throttler_->time_budget_pools_.erase(this);
154 }
155
156 bool TaskQueueThrottler::TimeBudgetPool::HasEnoughBudgetToRun(
157 base::TimeTicks now) {
158 Advance(now);
159 return !is_enabled_ || current_budget_level_.InMicroseconds() >= 0;
160 }
161
162 base::TimeTicks TaskQueueThrottler::TimeBudgetPool::GetNextAllowedRunTime() {
163 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) {
164 return last_checkpoint_;
165 } else {
166 // Subtract because current_budget is negative.
167 return last_checkpoint_ - current_budget_level_ / cpu_percentage_;
168 }
169 }
170
171 void TaskQueueThrottler::TimeBudgetPool::RecordTaskRunTime(
172 base::TimeDelta task_run_time) {
173 if (is_enabled_)
174 current_budget_level_ -= task_run_time;
175 }
176
177 const char* TaskQueueThrottler::TimeBudgetPool::Name() const {
178 return name_;
179 }
180
181 void TaskQueueThrottler::TimeBudgetPool::AsValueInto(
182 base::trace_event::TracedValue* state,
183 base::TimeTicks now) const {
184 state->BeginDictionary();
185
186 state->SetString("name", name_);
187 state->SetDouble("time_budget", cpu_percentage_);
188 state->SetDouble("time_budget_level_in_seconds",
189 current_budget_level_.InSecondsF());
190 state->SetDouble("last_checkpoint_seconds_ago",
191 (now - last_checkpoint_).InSecondsF());
192
193 state->BeginArray("task_queues");
194 for (TaskQueue* queue : associated_task_queues_) {
195 state->AppendString(base::StringPrintf(
196 "%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(queue))));
197 }
198 state->EndArray();
199
200 state->EndDictionary();
201 }
202
203 void TaskQueueThrottler::TimeBudgetPool::Advance(base::TimeTicks now) {
204 if (now > last_checkpoint_) {
205 if (is_enabled_) {
206 current_budget_level_ = std::min(
207 current_budget_level_ + cpu_percentage_ * (now - last_checkpoint_),
208 max_budget_level_);
209 }
210 last_checkpoint_ = now;
211 }
212 }
213
214 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues(
215 base::TimeTicks now) {
216 for (TaskQueue* queue : associated_task_queues_) {
217 if (!task_queue_throttler_->IsThrottled(queue))
218 continue;
219
220 queue->SetQueueEnabled(false);
221 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue,
222 base::nullopt);
223 }
224 }
225
226 TaskQueueThrottler::TaskQueueThrottler(
227 RendererSchedulerImpl* renderer_scheduler,
228 const char* tracing_category)
21 : task_runner_(renderer_scheduler->ControlTaskRunner()), 229 : task_runner_(renderer_scheduler->ControlTaskRunner()),
22 renderer_scheduler_(renderer_scheduler), 230 renderer_scheduler_(renderer_scheduler),
23 tick_clock_(renderer_scheduler->tick_clock()), 231 tick_clock_(renderer_scheduler->tick_clock()),
24 tracing_category_(tracing_category), 232 tracing_category_(tracing_category),
25 time_domain_(new ThrottledTimeDomain(this, tracing_category)), 233 time_domain_(new ThrottledTimeDomain(this, tracing_category)),
26 virtual_time_(false), 234 virtual_time_(false),
27 weak_factory_(this) { 235 weak_factory_(this) {
28 pump_throttled_tasks_closure_.Reset(base::Bind( 236 pump_throttled_tasks_closure_.Reset(base::Bind(
29 &ThrottlingHelper::PumpThrottledTasks, weak_factory_.GetWeakPtr())); 237 &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr()));
30 forward_immediate_work_closure_ = 238 forward_immediate_work_callback_ =
31 base::Bind(&ThrottlingHelper::OnTimeDomainHasImmediateWork, 239 base::Bind(&TaskQueueThrottler::OnTimeDomainHasImmediateWork,
32 weak_factory_.GetWeakPtr()); 240 weak_factory_.GetWeakPtr());
33 241
34 renderer_scheduler_->RegisterTimeDomain(time_domain_.get()); 242 renderer_scheduler_->RegisterTimeDomain(time_domain_.get());
35 } 243 }
36 244
37 ThrottlingHelper::~ThrottlingHelper() { 245 TaskQueueThrottler::~TaskQueueThrottler() {
38 // It's possible for queues to be still throttled, so we need to tidy up 246 // It's possible for queues to be still throttled, so we need to tidy up
39 // before unregistering the time domain. 247 // before unregistering the time domain.
40 for (const TaskQueueMap::value_type& map_entry : throttled_queues_) { 248 for (const TaskQueueMap::value_type& map_entry : queue_details_) {
41 TaskQueue* task_queue = map_entry.first; 249 if (map_entry.second.IsThrottled()) {
42 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); 250 TaskQueue* task_queue = map_entry.first;
43 task_queue->RemoveFence(); 251 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
252 task_queue->RemoveFence();
253 }
44 } 254 }
45 255
46 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get()); 256 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get());
47 } 257 }
48 258
49 void ThrottlingHelper::SetQueueEnabled(TaskQueue* task_queue, bool enabled) { 259 void TaskQueueThrottler::SetQueueEnabled(TaskQueue* task_queue, bool enabled) {
50 TaskQueueMap::iterator find_it = throttled_queues_.find(task_queue); 260 TaskQueueMap::iterator find_it = queue_details_.find(task_queue);
51 261
52 if (find_it == throttled_queues_.end()) { 262 if (find_it == queue_details_.end()) {
53 task_queue->SetQueueEnabled(enabled); 263 task_queue->SetQueueEnabled(enabled);
54 return; 264 return;
55 } 265 }
56 266
57 find_it->second.enabled = enabled; 267 find_it->second.enabled = enabled;
58 268
269 if (!find_it->second.IsThrottled())
270 return;
271
59 // We don't enable the queue here because it's throttled and there might be 272 // We don't enable the queue here because it's throttled and there might be
60 // tasks in it's work queue that would execute immediatly rather than after 273 // tasks in it's work queue that would execute immediatly rather than after
61 // PumpThrottledTasks runs. 274 // PumpThrottledTasks runs.
62 if (!enabled) 275 if (!enabled) {
63 task_queue->SetQueueEnabled(false); 276 task_queue->SetQueueEnabled(false);
64 } 277 MaybeSchedulePumpQueue(FROM_HERE, tick_clock_->NowTicks(), task_queue,
65 278 base::nullopt);
66 void ThrottlingHelper::IncreaseThrottleRefCount(TaskQueue* task_queue) { 279 }
280 }
281
282 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) {
67 DCHECK_NE(task_queue, task_runner_.get()); 283 DCHECK_NE(task_queue, task_runner_.get());
68 284
69 if (virtual_time_) 285 if (virtual_time_)
70 return; 286 return;
71 287
72 std::pair<TaskQueueMap::iterator, bool> insert_result = 288 std::pair<TaskQueueMap::iterator, bool> insert_result =
73 throttled_queues_.insert(std::make_pair( 289 queue_details_.insert(std::make_pair(task_queue, Metadata()));
74 task_queue, Metadata(1, task_queue->IsQueueEnabled()))); 290
75 291 if (!insert_result.first->second.IsThrottled()) {
76 if (insert_result.second) { 292 // The insert was successful so we need to throttle the queue.
77 // The insert was succesful so we need to throttle the queue. 293 insert_result.first->second.enabled = task_queue->IsQueueEnabled();
294
78 task_queue->SetTimeDomain(time_domain_.get()); 295 task_queue->SetTimeDomain(time_domain_.get());
79 task_queue->RemoveFence(); 296 task_queue->RemoveFence();
80 task_queue->SetQueueEnabled(false); 297 task_queue->SetQueueEnabled(false);
81 298
82 if (!task_queue->IsEmpty()) { 299 if (!task_queue->IsEmpty()) {
83 if (task_queue->HasPendingImmediateWork()) { 300 if (task_queue->HasPendingImmediateWork()) {
84 OnTimeDomainHasImmediateWork(); 301 OnTimeDomainHasImmediateWork(task_queue);
85 } else { 302 } else {
86 OnTimeDomainHasDelayedWork(); 303 OnTimeDomainHasDelayedWork(task_queue);
87 } 304 }
88 } 305 }
89 } else { 306
90 // An entry already existed in the map so we need to increment the refcount. 307 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
91 insert_result.first->second.throttling_ref_count++; 308 "task_queue", task_queue);
92 } 309 }
93 } 310
94 311 insert_result.first->second.throttling_ref_count++;
95 void ThrottlingHelper::DecreaseThrottleRefCount(TaskQueue* task_queue) { 312 }
313
314 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
96 if (virtual_time_) 315 if (virtual_time_)
97 return; 316 return;
98 317
99 TaskQueueMap::iterator iter = throttled_queues_.find(task_queue); 318 TaskQueueMap::iterator iter = queue_details_.find(task_queue);
100 319
101 if (iter != throttled_queues_.end() && 320 if (iter != queue_details_.end() &&
102 --iter->second.throttling_ref_count == 0) { 321 --iter->second.throttling_ref_count == 0) {
103 bool enabled = iter->second.enabled; 322 bool enabled = iter->second.enabled;
104 // The refcount has become zero, we need to unthrottle the queue. 323
105 throttled_queues_.erase(iter); 324 MaybeDeleteQueueMetadata(iter);
106 325
107 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); 326 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
108 task_queue->RemoveFence(); 327 task_queue->RemoveFence();
109 task_queue->SetQueueEnabled(enabled); 328 task_queue->SetQueueEnabled(enabled);
110 } 329
111 } 330 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUntrottled",
112 331 "task_queue", task_queue);
113 bool ThrottlingHelper::IsThrottled(TaskQueue* task_queue) const { 332 }
114 return throttled_queues_.find(task_queue) != throttled_queues_.end(); 333 }
115 } 334
116 335 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const {
117 void ThrottlingHelper::UnregisterTaskQueue(TaskQueue* task_queue) { 336 auto find_it = queue_details_.find(task_queue);
118 throttled_queues_.erase(task_queue); 337 if (find_it == queue_details_.end())
119 } 338 return false;
120 339 return find_it->second.IsThrottled();
121 void ThrottlingHelper::OnTimeDomainHasImmediateWork() { 340 }
122 // Forward to the main thread if called from another thread. 341
342 void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) {
343 LazyNow lazy_now(tick_clock_);
344 auto find_it = queue_details_.find(task_queue);
345
346 if (find_it == queue_details_.end())
347 return;
348
349 if (find_it->second.time_budget_pool)
350 find_it->second.time_budget_pool->RemoveQueue(lazy_now.Now(), task_queue);
351
352 queue_details_.erase(find_it);
353 }
354
355 void TaskQueueThrottler::OnTimeDomainHasImmediateWork(TaskQueue* queue) {
356 // Forward to the main thread if called from another thread
123 if (!task_runner_->RunsTasksOnCurrentThread()) { 357 if (!task_runner_->RunsTasksOnCurrentThread()) {
124 task_runner_->PostTask(FROM_HERE, forward_immediate_work_closure_); 358 task_runner_->PostTask(FROM_HERE,
359 base::Bind(forward_immediate_work_callback_, queue));
125 return; 360 return;
126 } 361 }
127 TRACE_EVENT0(tracing_category_, 362 TRACE_EVENT0(tracing_category_,
128 "ThrottlingHelper::OnTimeDomainHasImmediateWork"); 363 "TaskQueueThrottler::OnTimeDomainHasImmediateWork");
364
129 base::TimeTicks now = tick_clock_->NowTicks(); 365 base::TimeTicks now = tick_clock_->NowTicks();
130 MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, now, now); 366 base::TimeTicks next_allowed_run_time = GetNextAllowedRunTime(now, queue);
131 } 367 MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_allowed_run_time);
132 368 }
133 void ThrottlingHelper::OnTimeDomainHasDelayedWork() { 369
370 void TaskQueueThrottler::OnTimeDomainHasDelayedWork(TaskQueue* queue) {
134 TRACE_EVENT0(tracing_category_, 371 TRACE_EVENT0(tracing_category_,
135 "ThrottlingHelper::OnTimeDomainHasDelayedWork"); 372 "TaskQueueThrottler::OnTimeDomainHasDelayedWork");
136 base::TimeTicks next_scheduled_delayed_task;
137 bool has_delayed_task =
138 time_domain_->NextScheduledRunTime(&next_scheduled_delayed_task);
139 DCHECK(has_delayed_task);
140 base::TimeTicks now = tick_clock_->NowTicks(); 373 base::TimeTicks now = tick_clock_->NowTicks();
141 MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, now, 374 LazyNow lazy_now(now);
142 next_scheduled_delayed_task); 375
143 } 376 base::Optional<base::TimeTicks> next_scheduled_delayed_task =
144 377 NextTaskRunTime(&lazy_now, queue);
145 void ThrottlingHelper::PumpThrottledTasks() { 378 DCHECK(next_scheduled_delayed_task);
146 TRACE_EVENT0(tracing_category_, "ThrottlingHelper::PumpThrottledTasks"); 379 MaybeSchedulePumpThrottledTasks(FROM_HERE, now,
147 pending_pump_throttled_tasks_runtime_ = base::TimeTicks(); 380 next_scheduled_delayed_task.value());
148 381 }
149 for (const TaskQueueMap::value_type& map_entry : throttled_queues_) { 382
383 void TaskQueueThrottler::PumpThrottledTasks() {
384 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks");
385 pending_pump_throttled_tasks_runtime_.reset();
386
387 LazyNow lazy_now(tick_clock_);
388 base::Optional<base::TimeTicks> next_scheduled_delayed_task;
389
390 for (const TaskQueueMap::value_type& map_entry : queue_details_) {
150 TaskQueue* task_queue = map_entry.first; 391 TaskQueue* task_queue = map_entry.first;
151 if (!map_entry.second.enabled || task_queue->IsEmpty()) 392 if (!map_entry.second.enabled || task_queue->IsEmpty() ||
393 !map_entry.second.IsThrottled())
394 continue;
395
396 // Don't enable queues whose budget pool doesn't allow them to run now.
397 base::TimeTicks next_allowed_run_time =
398 GetNextAllowedRunTime(lazy_now.Now(), task_queue);
399 base::Optional<base::TimeTicks> next_desired_run_time =
400 NextTaskRunTime(&lazy_now, task_queue);
401
402 if (next_desired_run_time &&
403 next_allowed_run_time > next_desired_run_time.value()) {
404 TRACE_EVENT1(
405 tracing_category_,
406 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
407 "throttle_time_in_seconds",
408 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF());
409
410 // Schedule a pump for queue which was disabled because of time budget.
411 next_scheduled_delayed_task =
412 Min(next_scheduled_delayed_task, next_allowed_run_time);
413
414 continue;
415 }
416
417 next_scheduled_delayed_task =
418 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp());
419
420 if (next_allowed_run_time > lazy_now.Now())
152 continue; 421 continue;
153 422
154 task_queue->SetQueueEnabled(true); 423 task_queue->SetQueueEnabled(true);
155 task_queue->InsertFence(); 424 task_queue->InsertFence();
156 } 425 }
157 426
158 base::TimeTicks next_scheduled_delayed_task; 427 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is
159 // Maybe schedule a call to ThrottlingHelper::PumpThrottledTasks if there is 428 // a pending delayed task or a throttled task ready to run.
160 // a pending delayed task. NOTE posting a non-delayed task in the future will 429 // NOTE: posting a non-delayed task in the future will result in
161 // result in ThrottlingHelper::OnTimeDomainHasImmediateWork being called. 430 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called.
162 if (time_domain_->NextScheduledRunTime(&next_scheduled_delayed_task)) { 431 if (next_scheduled_delayed_task) {
163 LazyNow lazy_now(tick_clock_); 432 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(),
164 MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, lazy_now.Now(), 433 *next_scheduled_delayed_task);
165 next_scheduled_delayed_task);
166 } 434 }
167 } 435 }
168 436
169 /* static */ 437 /* static */
170 base::TimeTicks ThrottlingHelper::ThrottledRunTime( 438 base::TimeTicks TaskQueueThrottler::AlignedThrottledRunTime(
171 base::TimeTicks unthrottled_runtime) { 439 base::TimeTicks unthrottled_runtime) {
172 const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1); 440 const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1);
173 return unthrottled_runtime + one_second - 441 return unthrottled_runtime + one_second -
174 ((unthrottled_runtime - base::TimeTicks()) % one_second); 442 ((unthrottled_runtime - base::TimeTicks()) % one_second);
175 } 443 }
176 444
177 void ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked( 445 void TaskQueueThrottler::MaybeSchedulePumpThrottledTasks(
178 const tracked_objects::Location& from_here, 446 const tracked_objects::Location& from_here,
179 base::TimeTicks now, 447 base::TimeTicks now,
180 base::TimeTicks unthrottled_runtime) { 448 base::TimeTicks unaligned_runtime) {
181 if (virtual_time_) 449 if (virtual_time_)
182 return; 450 return;
183 451
184 base::TimeTicks throttled_runtime = 452 base::TimeTicks runtime =
185 ThrottledRunTime(std::max(now, unthrottled_runtime)); 453 std::max(now, AlignedThrottledRunTime(unaligned_runtime));
454
186 // If there is a pending call to PumpThrottledTasks and it's sooner than 455 // If there is a pending call to PumpThrottledTasks and it's sooner than
187 // |unthrottled_runtime| then return. 456 // |runtime| then return.
188 if (!pending_pump_throttled_tasks_runtime_.is_null() && 457 if (pending_pump_throttled_tasks_runtime_ &&
189 throttled_runtime >= pending_pump_throttled_tasks_runtime_) { 458 runtime >= pending_pump_throttled_tasks_runtime_.value()) {
190 return; 459 return;
191 } 460 }
192 461
193 pending_pump_throttled_tasks_runtime_ = throttled_runtime; 462 pending_pump_throttled_tasks_runtime_ = runtime;
194 463
195 pump_throttled_tasks_closure_.Cancel(); 464 pump_throttled_tasks_closure_.Cancel();
196 465
197 base::TimeDelta delay = pending_pump_throttled_tasks_runtime_ - now; 466 base::TimeDelta delay = pending_pump_throttled_tasks_runtime_.value() - now;
198 TRACE_EVENT1(tracing_category_, 467 TRACE_EVENT1(tracing_category_,
199 "ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked", 468 "TaskQueueThrottler::MaybeSchedulePumpThrottledTasks",
200 "delay_till_next_pump_ms", delay.InMilliseconds()); 469 "delay_till_next_pump_ms", delay.InMilliseconds());
201 task_runner_->PostDelayedTask( 470 task_runner_->PostDelayedTask(
202 from_here, pump_throttled_tasks_closure_.callback(), delay); 471 from_here, pump_throttled_tasks_closure_.callback(), delay);
203 } 472 }
204 473
205 void ThrottlingHelper::EnableVirtualTime() { 474 void TaskQueueThrottler::EnableVirtualTime() {
206 virtual_time_ = true; 475 virtual_time_ = true;
207 476
208 pump_throttled_tasks_closure_.Cancel(); 477 pump_throttled_tasks_closure_.Cancel();
209 478
210 while (!throttled_queues_.empty()) { 479 for (auto it = queue_details_.begin(); it != queue_details_.end();) {
211 TaskQueue* task_queue = throttled_queues_.begin()->first; 480 TaskQueue* task_queue = it->first;
212 bool enabled = throttled_queues_.begin()->second.enabled; 481 bool enabled = it->second.enabled;
213 482
214 throttled_queues_.erase(throttled_queues_.begin()); 483 if (!it->second.time_budget_pool) {
484 it = queue_details_.erase(it);
485 } else {
486 // Fall back to default values.
487 it->second.throttling_ref_count = 0;
488 it->second.enabled = false;
489 it++;
490 }
215 491
216 task_queue->SetTimeDomain(renderer_scheduler_->GetVirtualTimeDomain()); 492 task_queue->SetTimeDomain(renderer_scheduler_->GetVirtualTimeDomain());
217 task_queue->RemoveFence(); 493 task_queue->RemoveFence();
218 task_queue->SetQueueEnabled(enabled); 494 task_queue->SetQueueEnabled(enabled);
219 } 495 }
220 } 496 }
221 497
498 TaskQueueThrottler::TimeBudgetPool* TaskQueueThrottler::CreateTimeBudgetPool(
499 const char* name) {
500 TimeBudgetPool* time_budget_pool =
501 new TimeBudgetPool(name, this, tick_clock_->NowTicks());
502 time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool);
503 return time_budget_pool;
504 }
505
506 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
507 base::TimeTicks start_time,
508 base::TimeTicks end_time) {
509 if (!IsThrottled(task_queue))
510 return;
511
512 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue);
513 if (!time_budget_pool)
514 return;
515
516 time_budget_pool->RecordTaskRunTime(end_time - start_time);
517 if (!time_budget_pool->HasEnoughBudgetToRun(end_time))
518 time_budget_pool->BlockThrottledQueues(end_time);
519 }
520
521 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state,
522 base::TimeTicks now) const {
523 if (pending_pump_throttled_tasks_runtime_) {
524 state->SetDouble(
525 "next_throttled_tasks_pump_in_seconds",
526 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF());
527 }
528
529 state->BeginDictionary("time_budget_pools");
530
531 for (const auto& map_entry : time_budget_pools_) {
532 TaskQueueThrottler::TimeBudgetPool* pool = map_entry.first;
533 pool->AsValueInto(state, now);
534 }
535
536 state->EndDictionary();
537 }
538
539 TaskQueueThrottler::TimeBudgetPool*
540 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) {
541 auto find_it = queue_details_.find(queue);
542 if (find_it == queue_details_.end())
543 return nullptr;
544 return find_it->second.time_budget_pool;
545 }
546
547 void TaskQueueThrottler::MaybeSchedulePumpQueue(
548 const tracked_objects::Location& from_here,
549 base::TimeTicks now,
550 TaskQueue* queue,
551 base::Optional<base::TimeTicks> next_possible_run_time) {
552 LazyNow lazy_now(now);
553 base::Optional<base::TimeTicks> next_run_time =
554 Max(NextTaskRunTime(&lazy_now, queue), next_possible_run_time);
555
556 if (next_run_time) {
557 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value());
558 }
559 }
560
561 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now,
562 TaskQueue* queue) {
563 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(queue);
564 if (!time_budget_pool)
565 return now;
566 return std::max(now, time_budget_pool->GetNextAllowedRunTime());
567 }
568
569 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) {
570 if (!it->second.IsThrottled() && !it->second.time_budget_pool)
571 queue_details_.erase(it);
572 }
573
222 } // namespace scheduler 574 } // namespace scheduler
223 } // namespace blink 575 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698