Run task queue manager work in batches
[chromium-blink-merge.git] / content / renderer / scheduler / renderer_scheduler_impl.cc
blobfbd8cbe605c07dfd3b1a47f62f29c45e33b85fee
1 // Copyright 2014 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.
5 #include "content/renderer/scheduler/renderer_scheduler_impl.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "base/trace_event/trace_event.h"
10 #include "base/trace_event/trace_event_argument.h"
11 #include "cc/output/begin_frame_args.h"
12 #include "content/renderer/scheduler/renderer_task_queue_selector.h"
13 #include "ui/gfx/frame_time.h"
15 namespace content {
17 RendererSchedulerImpl::RendererSchedulerImpl(
18 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
19 : renderer_task_queue_selector_(new RendererTaskQueueSelector()),
20 task_queue_manager_(
21 new TaskQueueManager(TASK_QUEUE_COUNT,
22 main_task_runner,
23 renderer_task_queue_selector_.get())),
24 control_task_runner_(
25 task_queue_manager_->TaskRunnerForQueue(CONTROL_TASK_QUEUE)),
26 default_task_runner_(
27 task_queue_manager_->TaskRunnerForQueue(DEFAULT_TASK_QUEUE)),
28 compositor_task_runner_(
29 task_queue_manager_->TaskRunnerForQueue(COMPOSITOR_TASK_QUEUE)),
30 loading_task_runner_(
31 task_queue_manager_->TaskRunnerForQueue(LOADING_TASK_QUEUE)),
32 current_policy_(NORMAL_PRIORITY_POLICY),
33 policy_may_need_update_(&incoming_signals_lock_),
34 weak_factory_(this) {
35 weak_renderer_scheduler_ptr_ = weak_factory_.GetWeakPtr();
36 update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy,
37 weak_renderer_scheduler_ptr_);
38 end_idle_period_closure_.Reset(base::Bind(
39 &RendererSchedulerImpl::EndIdlePeriod, weak_renderer_scheduler_ptr_));
40 idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner(
41 task_queue_manager_->TaskRunnerForQueue(IDLE_TASK_QUEUE),
42 base::Bind(&RendererSchedulerImpl::CurrentIdleTaskDeadlineCallback,
43 weak_renderer_scheduler_ptr_)));
44 renderer_task_queue_selector_->SetQueuePriority(
45 CONTROL_TASK_QUEUE, RendererTaskQueueSelector::CONTROL_PRIORITY);
46 renderer_task_queue_selector_->DisableQueue(IDLE_TASK_QUEUE);
47 task_queue_manager_->SetAutoPump(IDLE_TASK_QUEUE, false);
48 // TODO(skyostil): Increase this to 4 (crbug.com/444764).
49 task_queue_manager_->SetWorkBatchSize(1);
51 for (size_t i = 0; i < TASK_QUEUE_COUNT; i++) {
52 task_queue_manager_->SetQueueName(
53 i, TaskQueueIdToString(static_cast<QueueId>(i)));
55 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
56 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
57 this);
60 RendererSchedulerImpl::~RendererSchedulerImpl() {
61 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
62 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
63 this);
66 void RendererSchedulerImpl::Shutdown() {
67 main_thread_checker_.CalledOnValidThread();
68 task_queue_manager_.reset();
71 scoped_refptr<base::SingleThreadTaskRunner>
72 RendererSchedulerImpl::DefaultTaskRunner() {
73 main_thread_checker_.CalledOnValidThread();
74 return default_task_runner_;
77 scoped_refptr<base::SingleThreadTaskRunner>
78 RendererSchedulerImpl::CompositorTaskRunner() {
79 main_thread_checker_.CalledOnValidThread();
80 return compositor_task_runner_;
83 scoped_refptr<SingleThreadIdleTaskRunner>
84 RendererSchedulerImpl::IdleTaskRunner() {
85 main_thread_checker_.CalledOnValidThread();
86 return idle_task_runner_;
89 scoped_refptr<base::SingleThreadTaskRunner>
90 RendererSchedulerImpl::LoadingTaskRunner() {
91 main_thread_checker_.CalledOnValidThread();
92 return loading_task_runner_;
95 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs& args) {
96 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
97 "RendererSchedulerImpl::WillBeginFrame", "args", args.AsValue());
98 main_thread_checker_.CalledOnValidThread();
99 if (!task_queue_manager_)
100 return;
102 EndIdlePeriod();
103 estimated_next_frame_begin_ = args.frame_time + args.interval;
106 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
107 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
108 "RendererSchedulerImpl::DidCommitFrameToCompositor");
109 main_thread_checker_.CalledOnValidThread();
110 if (!task_queue_manager_)
111 return;
113 base::TimeTicks now(Now());
114 if (now < estimated_next_frame_begin_) {
115 StartIdlePeriod();
116 control_task_runner_->PostDelayedTask(FROM_HERE,
117 end_idle_period_closure_.callback(),
118 estimated_next_frame_begin_ - now);
122 void RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread(
123 blink::WebInputEvent::Type type) {
124 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
125 "RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread");
126 // Ignore mouse events because on windows these can very frequent.
127 // Ignore keyboard events because it doesn't really make sense to enter
128 // compositor priority for them.
129 if (blink::WebInputEvent::isMouseEventType(type) ||
130 blink::WebInputEvent::isKeyboardEventType(type)) {
131 return;
133 UpdateForInputEvent();
136 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
137 UpdateForInputEvent();
140 void RendererSchedulerImpl::UpdateForInputEvent() {
141 base::AutoLock lock(incoming_signals_lock_);
142 if (last_input_time_.is_null()) {
143 // Update scheduler policy if should start a new compositor policy mode.
144 policy_may_need_update_.SetLocked(true);
145 PostUpdatePolicyOnControlRunner(base::TimeDelta());
147 last_input_time_ = Now();
150 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
151 main_thread_checker_.CalledOnValidThread();
152 if (!task_queue_manager_)
153 return false;
155 MaybeUpdatePolicy();
156 // We only yield if we are in the compositor priority and there is compositor
157 // work outstanding. Note: even though the control queue is higher priority
158 // we don't yield for it since these tasks are not user-provided work and they
159 // are only intended to run before the next task, not interrupt the tasks.
160 return SchedulerPolicy() == COMPOSITOR_PRIORITY_POLICY &&
161 !task_queue_manager_->IsQueueEmpty(COMPOSITOR_TASK_QUEUE);
164 void RendererSchedulerImpl::CurrentIdleTaskDeadlineCallback(
165 base::TimeTicks* deadline_out) const {
166 main_thread_checker_.CalledOnValidThread();
167 *deadline_out = estimated_next_frame_begin_;
170 RendererSchedulerImpl::Policy RendererSchedulerImpl::SchedulerPolicy() const {
171 main_thread_checker_.CalledOnValidThread();
172 return current_policy_;
175 void RendererSchedulerImpl::MaybeUpdatePolicy() {
176 main_thread_checker_.CalledOnValidThread();
177 if (policy_may_need_update_.IsSet()) {
178 UpdatePolicy();
182 void RendererSchedulerImpl::PostUpdatePolicyOnControlRunner(
183 base::TimeDelta delay) {
184 control_task_runner_->PostDelayedTask(
185 FROM_HERE, update_policy_closure_, delay);
188 void RendererSchedulerImpl::UpdatePolicy() {
189 main_thread_checker_.CalledOnValidThread();
190 if (!task_queue_manager_)
191 return;
193 base::AutoLock lock(incoming_signals_lock_);
194 base::TimeTicks now;
195 policy_may_need_update_.SetLocked(false);
197 Policy new_policy = NORMAL_PRIORITY_POLICY;
198 if (!last_input_time_.is_null()) {
199 base::TimeDelta compositor_priority_duration =
200 base::TimeDelta::FromMilliseconds(kCompositorPriorityAfterTouchMillis);
201 base::TimeTicks compositor_priority_end(last_input_time_ +
202 compositor_priority_duration);
203 now = Now();
204 if (compositor_priority_end > now) {
205 PostUpdatePolicyOnControlRunner(compositor_priority_end - now);
206 new_policy = COMPOSITOR_PRIORITY_POLICY;
207 } else {
208 // Null out last_input_time_ to ensure
209 // DidReceiveInputEventOnCompositorThread will post an
210 // UpdatePolicy task when it's next called.
211 last_input_time_ = base::TimeTicks();
215 if (new_policy == current_policy_)
216 return;
218 switch (new_policy) {
219 case COMPOSITOR_PRIORITY_POLICY:
220 renderer_task_queue_selector_->SetQueuePriority(
221 COMPOSITOR_TASK_QUEUE, RendererTaskQueueSelector::HIGH_PRIORITY);
222 // TODO(scheduler-dev): Add a task priority between HIGH and BEST_EFFORT
223 // that still has some guarantee of running.
224 renderer_task_queue_selector_->SetQueuePriority(
225 LOADING_TASK_QUEUE, RendererTaskQueueSelector::BEST_EFFORT_PRIORITY);
226 break;
227 case NORMAL_PRIORITY_POLICY:
228 renderer_task_queue_selector_->SetQueuePriority(
229 COMPOSITOR_TASK_QUEUE, RendererTaskQueueSelector::NORMAL_PRIORITY);
230 renderer_task_queue_selector_->SetQueuePriority(
231 LOADING_TASK_QUEUE, RendererTaskQueueSelector::NORMAL_PRIORITY);
232 break;
234 current_policy_ = new_policy;
236 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
237 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
238 this, AsValueLocked(now));
239 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
240 "RendererScheduler.policy", current_policy_);
243 void RendererSchedulerImpl::StartIdlePeriod() {
244 TRACE_EVENT_ASYNC_BEGIN0("renderer.scheduler",
245 "RendererSchedulerIdlePeriod", this);
246 main_thread_checker_.CalledOnValidThread();
247 renderer_task_queue_selector_->EnableQueue(
248 IDLE_TASK_QUEUE, RendererTaskQueueSelector::BEST_EFFORT_PRIORITY);
249 task_queue_manager_->PumpQueue(IDLE_TASK_QUEUE);
252 void RendererSchedulerImpl::EndIdlePeriod() {
253 TRACE_EVENT_ASYNC_END0("renderer.scheduler",
254 "RendererSchedulerIdlePeriod", this);
255 main_thread_checker_.CalledOnValidThread();
256 end_idle_period_closure_.Cancel();
257 renderer_task_queue_selector_->DisableQueue(IDLE_TASK_QUEUE);
260 void RendererSchedulerImpl::SetTimeSourceForTesting(
261 scoped_refptr<cc::TestNowSource> time_source) {
262 main_thread_checker_.CalledOnValidThread();
263 time_source_ = time_source;
264 task_queue_manager_->SetTimeSourceForTesting(time_source);
267 base::TimeTicks RendererSchedulerImpl::Now() const {
268 return UNLIKELY(time_source_) ? time_source_->Now() : base::TimeTicks::Now();
271 RendererSchedulerImpl::PollableNeedsUpdateFlag::PollableNeedsUpdateFlag(
272 base::Lock* write_lock_)
273 : flag_(false), write_lock_(write_lock_) {
276 RendererSchedulerImpl::PollableNeedsUpdateFlag::~PollableNeedsUpdateFlag() {
279 void RendererSchedulerImpl::PollableNeedsUpdateFlag::SetLocked(bool value) {
280 write_lock_->AssertAcquired();
281 base::subtle::Release_Store(&flag_, value);
284 bool RendererSchedulerImpl::PollableNeedsUpdateFlag::IsSet() const {
285 thread_checker_.CalledOnValidThread();
286 return base::subtle::Acquire_Load(&flag_) != 0;
289 // static
290 const char* RendererSchedulerImpl::TaskQueueIdToString(QueueId queue_id) {
291 switch (queue_id) {
292 case DEFAULT_TASK_QUEUE:
293 return "default_tq";
294 case COMPOSITOR_TASK_QUEUE:
295 return "compositor_tq";
296 case IDLE_TASK_QUEUE:
297 return "idle_tq";
298 case CONTROL_TASK_QUEUE:
299 return "control_tq";
300 case LOADING_TASK_QUEUE:
301 return "loading_tq";
302 default:
303 NOTREACHED();
304 return nullptr;
308 // static
309 const char* RendererSchedulerImpl::PolicyToString(Policy policy) {
310 switch (policy) {
311 case NORMAL_PRIORITY_POLICY:
312 return "normal";
313 case COMPOSITOR_PRIORITY_POLICY:
314 return "compositor";
315 default:
316 NOTREACHED();
317 return nullptr;
321 scoped_refptr<base::debug::ConvertableToTraceFormat>
322 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const {
323 main_thread_checker_.CalledOnValidThread();
324 incoming_signals_lock_.AssertAcquired();
326 if (optional_now.is_null())
327 optional_now = Now();
328 scoped_refptr<base::debug::TracedValue> state =
329 new base::debug::TracedValue();
331 state->SetString("current_policy", PolicyToString(current_policy_));
332 state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF());
333 state->SetDouble("last_input_time",
334 (last_input_time_ - base::TimeTicks()).InMillisecondsF());
335 state->SetDouble(
336 "estimated_next_frame_begin",
337 (estimated_next_frame_begin_ - base::TimeTicks()).InMillisecondsF());
339 return state;
342 } // namespace content