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

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

Powered by Google App Engine
This is Rietveld 408576698