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

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

Issue 2258133002: [scheduler] Implement time-based cpu throttling. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Use double instead of base::TimeTicks 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "platform/scheduler/renderer/throttling_helper.h"
6
7 #include "base/logging.h"
8 #include "platform/scheduler/base/real_time_domain.h"
9 #include "platform/scheduler/child/scheduler_tqm_delegate.h"
10 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
11 #include "platform/scheduler/renderer/renderer_scheduler_impl.h"
12 #include "platform/scheduler/renderer/throttled_time_domain.h"
13 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h"
14 #include "public/platform/WebFrameScheduler.h"
15
16 namespace blink {
17 namespace scheduler {
18
19 ThrottlingHelper::ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler,
20 const char* tracing_category)
21 : task_runner_(renderer_scheduler->ControlTaskRunner()),
22 renderer_scheduler_(renderer_scheduler),
23 tick_clock_(renderer_scheduler->tick_clock()),
24 tracing_category_(tracing_category),
25 time_domain_(new ThrottledTimeDomain(this, tracing_category)),
26 virtual_time_(false),
27 weak_factory_(this) {
28 pump_throttled_tasks_closure_.Reset(base::Bind(
29 &ThrottlingHelper::PumpThrottledTasks, weak_factory_.GetWeakPtr()));
30 forward_immediate_work_closure_ =
31 base::Bind(&ThrottlingHelper::OnTimeDomainHasImmediateWork,
32 weak_factory_.GetWeakPtr());
33
34 renderer_scheduler_->RegisterTimeDomain(time_domain_.get());
35 }
36
37 ThrottlingHelper::~ThrottlingHelper() {
38 // It's possible for queues to be still throttled, so we need to tidy up
39 // before unregistering the time domain.
40 for (const TaskQueueMap::value_type& map_entry : throttled_queues_) {
41 TaskQueue* task_queue = map_entry.first;
42 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
43 task_queue->RemoveFence();
44 }
45
46 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get());
47 }
48
49 void ThrottlingHelper::SetQueueEnabled(TaskQueue* task_queue, bool enabled) {
50 TaskQueueMap::iterator find_it = throttled_queues_.find(task_queue);
51
52 if (find_it == throttled_queues_.end()) {
53 task_queue->SetQueueEnabled(enabled);
54 return;
55 }
56
57 find_it->second.enabled = enabled;
58
59 // 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
61 // PumpThrottledTasks runs.
62 if (!enabled)
63 task_queue->SetQueueEnabled(false);
64 }
65
66 void ThrottlingHelper::IncreaseThrottleRefCount(TaskQueue* task_queue) {
67 DCHECK_NE(task_queue, task_runner_.get());
68
69 if (virtual_time_)
70 return;
71
72 std::pair<TaskQueueMap::iterator, bool> insert_result =
73 throttled_queues_.insert(std::make_pair(
74 task_queue, Metadata(1, task_queue->IsQueueEnabled())));
75
76 if (insert_result.second) {
77 // The insert was succesful so we need to throttle the queue.
78 task_queue->SetTimeDomain(time_domain_.get());
79 task_queue->RemoveFence();
80 task_queue->SetQueueEnabled(false);
81
82 if (!task_queue->IsEmpty()) {
83 if (task_queue->HasPendingImmediateWork()) {
84 OnTimeDomainHasImmediateWork();
85 } else {
86 OnTimeDomainHasDelayedWork();
87 }
88 }
89 } else {
90 // An entry already existed in the map so we need to increment the refcount.
91 insert_result.first->second.throttling_ref_count++;
92 }
93 }
94
95 void ThrottlingHelper::DecreaseThrottleRefCount(TaskQueue* task_queue) {
96 if (virtual_time_)
97 return;
98
99 TaskQueueMap::iterator iter = throttled_queues_.find(task_queue);
100
101 if (iter != throttled_queues_.end() &&
102 --iter->second.throttling_ref_count == 0) {
103 bool enabled = iter->second.enabled;
104 // The refcount has become zero, we need to unthrottle the queue.
105 throttled_queues_.erase(iter);
106
107 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
108 task_queue->RemoveFence();
109 task_queue->SetQueueEnabled(enabled);
110 }
111 }
112
113 bool ThrottlingHelper::IsThrottled(TaskQueue* task_queue) const {
114 return throttled_queues_.find(task_queue) != throttled_queues_.end();
115 }
116
117 void ThrottlingHelper::UnregisterTaskQueue(TaskQueue* task_queue) {
118 throttled_queues_.erase(task_queue);
119 }
120
121 void ThrottlingHelper::OnTimeDomainHasImmediateWork() {
122 // Forward to the main thread if called from another thread.
123 if (!task_runner_->RunsTasksOnCurrentThread()) {
124 task_runner_->PostTask(FROM_HERE, forward_immediate_work_closure_);
125 return;
126 }
127 TRACE_EVENT0(tracing_category_,
128 "ThrottlingHelper::OnTimeDomainHasImmediateWork");
129 base::TimeTicks now = tick_clock_->NowTicks();
130 MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, now, now);
131 }
132
133 void ThrottlingHelper::OnTimeDomainHasDelayedWork() {
134 TRACE_EVENT0(tracing_category_,
135 "ThrottlingHelper::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();
141 MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, now,
142 next_scheduled_delayed_task);
143 }
144
145 void ThrottlingHelper::PumpThrottledTasks() {
146 TRACE_EVENT0(tracing_category_, "ThrottlingHelper::PumpThrottledTasks");
147 pending_pump_throttled_tasks_runtime_ = base::TimeTicks();
148
149 LazyNow lazy_low(tick_clock_);
150 for (const TaskQueueMap::value_type& map_entry : throttled_queues_) {
151 TaskQueue* task_queue = map_entry.first;
152 if (!map_entry.second.enabled || task_queue->IsEmpty())
153 continue;
154
155 task_queue->SetQueueEnabled(true);
156 task_queue->InsertFence();
157 }
158 // Make sure NextScheduledRunTime gives us an up-to date result.
159 time_domain_->ClearExpiredWakeups();
160
161 base::TimeTicks next_scheduled_delayed_task;
162 // Maybe schedule a call to ThrottlingHelper::PumpThrottledTasks if there is
163 // a pending delayed task. NOTE posting a non-delayed task in the future will
164 // result in ThrottlingHelper::OnTimeDomainHasImmediateWork being called.
165 if (time_domain_->NextScheduledRunTime(&next_scheduled_delayed_task)) {
166 MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, lazy_low.Now(),
167 next_scheduled_delayed_task);
168 }
169 }
170
171 /* static */
172 base::TimeTicks ThrottlingHelper::ThrottledRunTime(
173 base::TimeTicks unthrottled_runtime) {
174 const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1);
175 return unthrottled_runtime + one_second -
176 ((unthrottled_runtime - base::TimeTicks()) % one_second);
177 }
178
179 void ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked(
180 const tracked_objects::Location& from_here,
181 base::TimeTicks now,
182 base::TimeTicks unthrottled_runtime) {
183 if (virtual_time_)
184 return;
185
186 base::TimeTicks throttled_runtime =
187 ThrottledRunTime(std::max(now, unthrottled_runtime));
188 // If there is a pending call to PumpThrottledTasks and it's sooner than
189 // |unthrottled_runtime| then return.
190 if (!pending_pump_throttled_tasks_runtime_.is_null() &&
191 throttled_runtime >= pending_pump_throttled_tasks_runtime_) {
192 return;
193 }
194
195 pending_pump_throttled_tasks_runtime_ = throttled_runtime;
196
197 pump_throttled_tasks_closure_.Cancel();
198
199 base::TimeDelta delay = pending_pump_throttled_tasks_runtime_ - now;
200 TRACE_EVENT1(tracing_category_,
201 "ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked",
202 "delay_till_next_pump_ms", delay.InMilliseconds());
203 task_runner_->PostDelayedTask(
204 from_here, pump_throttled_tasks_closure_.callback(), delay);
205 }
206
207 void ThrottlingHelper::EnableVirtualTime() {
208 virtual_time_ = true;
209
210 pump_throttled_tasks_closure_.Cancel();
211
212 while (!throttled_queues_.empty()) {
213 TaskQueue* task_queue = throttled_queues_.begin()->first;
214 bool enabled = throttled_queues_.begin()->second.enabled;
215
216 throttled_queues_.erase(throttled_queues_.begin());
217
218 task_queue->SetTimeDomain(renderer_scheduler_->GetVirtualTimeDomain());
219 task_queue->RemoveFence();
220 task_queue->SetQueueEnabled(enabled);
221 }
222 }
223
224 } // namespace scheduler
225 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698