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 "components/scheduler/renderer/renderer_scheduler_impl.h"
8 #include "base/debug/stack_trace.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 "components/scheduler/child/scheduler_task_runner_delegate.h"
13 #include "components/scheduler/child/task_queue_impl.h"
14 #include "components/scheduler/child/task_queue_selector.h"
18 const int kLoadingTaskEstimationSampleCount
= 200;
19 const double kLoadingTaskEstimationPercentile
= 90;
20 const int kTimerTaskEstimationSampleCount
= 200;
21 const double kTimerTaskEstimationPercentile
= 90;
22 const int kShortIdlePeriodDurationSampleCount
= 10;
23 const double kShortIdlePeriodDurationPercentile
= 20;
26 RendererSchedulerImpl::RendererSchedulerImpl(
27 scoped_refptr
<SchedulerTaskRunnerDelegate
> main_task_runner
)
28 : helper_(main_task_runner
,
30 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
31 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")),
32 idle_helper_(&helper_
,
35 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
36 "RendererSchedulerIdlePeriod",
38 control_task_runner_(helper_
.ControlTaskRunner()),
39 compositor_task_runner_(
40 helper_
.NewTaskQueue(TaskQueue::Spec("compositor_tq")
41 .SetShouldMonitorQuiescence(true))),
43 helper_
.NewTaskQueue(TaskQueue::Spec("loading_tq")
44 .SetShouldMonitorQuiescence(true))),
46 helper_
.NewTaskQueue(TaskQueue::Spec("timer_tq")
47 .SetShouldMonitorQuiescence(true))),
48 delayed_update_policy_runner_(
49 base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
50 base::Unretained(this)),
51 helper_
.ControlTaskRunner()),
52 policy_may_need_update_(&any_thread_lock_
),
54 update_policy_closure_
= base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
55 weak_factory_
.GetWeakPtr());
56 end_renderer_hidden_idle_period_closure_
.Reset(base::Bind(
57 &RendererSchedulerImpl::EndIdlePeriod
, weak_factory_
.GetWeakPtr()));
59 suspend_timers_when_backgrounded_closure_
.Reset(
60 base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded
,
61 weak_factory_
.GetWeakPtr()));
63 loading_task_runner_
->AddTaskObserver(
64 &MainThreadOnly().loading_task_cost_estimator
);
66 timer_task_runner_
->AddTaskObserver(
67 &MainThreadOnly().timer_task_cost_estimator
);
69 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
70 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
73 // Make sure that we don't initially assume there is no idle time.
74 MainThreadOnly().short_idle_period_duration
.InsertSample(
75 cc::BeginFrameArgs::DefaultInterval());
78 RendererSchedulerImpl::~RendererSchedulerImpl() {
79 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
80 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
82 timer_task_runner_
->RemoveTaskObserver(
83 &MainThreadOnly().timer_task_cost_estimator
);
84 loading_task_runner_
->RemoveTaskObserver(
85 &MainThreadOnly().loading_task_cost_estimator
);
86 // Ensure the renderer scheduler was shut down explicitly, because otherwise
87 // we could end up having stale pointers to the Blink heap which has been
88 // terminated by this point.
89 DCHECK(MainThreadOnly().was_shutdown
);
92 RendererSchedulerImpl::Policy::Policy()
93 : compositor_queue_priority(TaskQueue::NORMAL_PRIORITY
),
94 loading_queue_priority(TaskQueue::NORMAL_PRIORITY
),
95 timer_queue_priority(TaskQueue::NORMAL_PRIORITY
),
96 default_queue_priority(TaskQueue::NORMAL_PRIORITY
) {}
98 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly()
99 : loading_task_cost_estimator(kLoadingTaskEstimationSampleCount
,
100 kLoadingTaskEstimationPercentile
),
101 timer_task_cost_estimator(kTimerTaskEstimationSampleCount
,
102 kTimerTaskEstimationPercentile
),
103 short_idle_period_duration(kShortIdlePeriodDurationSampleCount
),
104 current_use_case(UseCase::NONE
),
105 timer_queue_suspend_count(0),
106 renderer_hidden(false),
107 renderer_backgrounded(false),
108 timer_queue_suspension_when_backgrounded_enabled(false),
109 timer_queue_suspended_when_backgrounded(false),
111 loading_tasks_seem_expensive(false),
112 timer_tasks_seem_expensive(false),
113 touchstart_expected_soon(false),
114 have_seen_a_begin_main_frame(false) {}
116 RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
118 RendererSchedulerImpl::AnyThread::AnyThread()
119 : awaiting_touch_start_response(false),
120 in_idle_period(false),
121 begin_main_frame_on_critical_path(false) {}
123 RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
124 : last_input_type(blink::WebInputEvent::Undefined
) {}
126 RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {
129 void RendererSchedulerImpl::Shutdown() {
131 MainThreadOnly().was_shutdown
= true;
134 scoped_refptr
<TaskQueue
> RendererSchedulerImpl::DefaultTaskRunner() {
135 return helper_
.DefaultTaskRunner();
138 scoped_refptr
<base::SingleThreadTaskRunner
>
139 RendererSchedulerImpl::CompositorTaskRunner() {
140 helper_
.CheckOnValidThread();
141 return compositor_task_runner_
;
144 scoped_refptr
<SingleThreadIdleTaskRunner
>
145 RendererSchedulerImpl::IdleTaskRunner() {
146 return idle_helper_
.IdleTaskRunner();
149 scoped_refptr
<base::SingleThreadTaskRunner
>
150 RendererSchedulerImpl::LoadingTaskRunner() {
151 helper_
.CheckOnValidThread();
152 return loading_task_runner_
;
155 scoped_refptr
<TaskQueue
> RendererSchedulerImpl::TimerTaskRunner() {
156 helper_
.CheckOnValidThread();
157 return timer_task_runner_
;
160 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
161 return idle_helper_
.CanExceedIdleDeadlineIfRequired();
164 void RendererSchedulerImpl::AddTaskObserver(
165 base::MessageLoop::TaskObserver
* task_observer
) {
166 helper_
.AddTaskObserver(task_observer
);
169 void RendererSchedulerImpl::RemoveTaskObserver(
170 base::MessageLoop::TaskObserver
* task_observer
) {
171 helper_
.RemoveTaskObserver(task_observer
);
174 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs
& args
) {
175 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
176 "RendererSchedulerImpl::WillBeginFrame", "args", args
.AsValue());
177 helper_
.CheckOnValidThread();
178 if (helper_
.IsShutdown())
182 MainThreadOnly().estimated_next_frame_begin
= args
.frame_time
+ args
.interval
;
183 MainThreadOnly().have_seen_a_begin_main_frame
= true;
185 base::AutoLock
lock(any_thread_lock_
);
186 AnyThread().begin_main_frame_on_critical_path
= args
.on_critical_path
;
190 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
191 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
192 "RendererSchedulerImpl::DidCommitFrameToCompositor");
193 helper_
.CheckOnValidThread();
194 if (helper_
.IsShutdown())
197 base::TimeTicks
now(helper_
.Now());
198 if (now
< MainThreadOnly().estimated_next_frame_begin
) {
199 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
200 // the next pending delayed tasks (as currently done in for long idle times)
201 idle_helper_
.StartIdlePeriod(
202 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD
, now
,
203 MainThreadOnly().estimated_next_frame_begin
);
204 MainThreadOnly().short_idle_period_duration
.InsertSample(
205 MainThreadOnly().estimated_next_frame_begin
- now
);
207 // There was no idle time :(
208 MainThreadOnly().short_idle_period_duration
.InsertSample(base::TimeDelta());
211 MainThreadOnly().expected_short_idle_period_duration
=
212 MainThreadOnly().short_idle_period_duration
.Percentile(
213 kShortIdlePeriodDurationPercentile
);
216 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
217 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
218 "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
219 helper_
.CheckOnValidThread();
220 if (helper_
.IsShutdown())
223 idle_helper_
.EnableLongIdlePeriod();
226 void RendererSchedulerImpl::OnRendererHidden() {
227 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
228 "RendererSchedulerImpl::OnRendererHidden");
229 helper_
.CheckOnValidThread();
230 if (helper_
.IsShutdown() || MainThreadOnly().renderer_hidden
)
233 idle_helper_
.EnableLongIdlePeriod();
235 // Ensure that we stop running idle tasks after a few seconds of being hidden.
236 end_renderer_hidden_idle_period_closure_
.Cancel();
237 base::TimeDelta end_idle_when_hidden_delay
=
238 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis
);
239 control_task_runner_
->PostDelayedTask(
240 FROM_HERE
, end_renderer_hidden_idle_period_closure_
.callback(),
241 end_idle_when_hidden_delay
);
242 MainThreadOnly().renderer_hidden
= true;
244 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
245 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
246 this, AsValue(helper_
.Now()));
249 void RendererSchedulerImpl::OnRendererVisible() {
250 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
251 "RendererSchedulerImpl::OnRendererVisible");
252 helper_
.CheckOnValidThread();
253 if (helper_
.IsShutdown() || !MainThreadOnly().renderer_hidden
)
256 end_renderer_hidden_idle_period_closure_
.Cancel();
257 MainThreadOnly().renderer_hidden
= false;
260 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
261 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
262 this, AsValue(helper_
.Now()));
265 void RendererSchedulerImpl::OnRendererBackgrounded() {
266 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
267 "RendererSchedulerImpl::OnRendererBackgrounded");
268 helper_
.CheckOnValidThread();
269 if (helper_
.IsShutdown() || MainThreadOnly().renderer_backgrounded
)
272 MainThreadOnly().renderer_backgrounded
= true;
273 if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled
)
276 suspend_timers_when_backgrounded_closure_
.Cancel();
277 base::TimeDelta suspend_timers_when_backgrounded_delay
=
278 base::TimeDelta::FromMilliseconds(
279 kSuspendTimersWhenBackgroundedDelayMillis
);
280 control_task_runner_
->PostDelayedTask(
281 FROM_HERE
, suspend_timers_when_backgrounded_closure_
.callback(),
282 suspend_timers_when_backgrounded_delay
);
285 void RendererSchedulerImpl::OnRendererForegrounded() {
286 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
287 "RendererSchedulerImpl::OnRendererForegrounded");
288 helper_
.CheckOnValidThread();
289 if (helper_
.IsShutdown() || !MainThreadOnly().renderer_backgrounded
)
292 MainThreadOnly().renderer_backgrounded
= false;
293 suspend_timers_when_backgrounded_closure_
.Cancel();
294 ResumeTimerQueueWhenForegrounded();
297 void RendererSchedulerImpl::EndIdlePeriod() {
298 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
299 "RendererSchedulerImpl::EndIdlePeriod");
300 helper_
.CheckOnValidThread();
301 idle_helper_
.EndIdlePeriod();
305 bool RendererSchedulerImpl::ShouldPrioritizeInputEvent(
306 const blink::WebInputEvent
& web_input_event
) {
307 // We regard MouseMove events with the left mouse button down as a signal
308 // that the user is doing something requiring a smooth frame rate.
309 if (web_input_event
.type
== blink::WebInputEvent::MouseMove
&&
310 (web_input_event
.modifiers
& blink::WebInputEvent::LeftButtonDown
)) {
313 // Ignore all other mouse events because they probably don't signal user
314 // interaction needing a smooth framerate. NOTE isMouseEventType returns false
315 // for mouse wheel events, hence we regard them as user input.
316 // Ignore keyboard events because it doesn't really make sense to enter
317 // compositor priority for them.
318 if (blink::WebInputEvent::isMouseEventType(web_input_event
.type
) ||
319 blink::WebInputEvent::isKeyboardEventType(web_input_event
.type
)) {
325 void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread(
326 const blink::WebInputEvent
& web_input_event
,
327 InputEventState event_state
) {
328 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
329 "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread");
330 if (!ShouldPrioritizeInputEvent(web_input_event
))
333 UpdateForInputEventOnCompositorThread(web_input_event
.type
, event_state
);
336 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
337 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
338 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread");
339 UpdateForInputEventOnCompositorThread(
340 blink::WebInputEvent::Undefined
,
341 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR
);
344 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread(
345 blink::WebInputEvent::Type type
,
346 InputEventState input_event_state
) {
347 base::AutoLock
lock(any_thread_lock_
);
348 base::TimeTicks now
= helper_
.Now();
350 // TODO(alexclarke): Move WebInputEventTraits where we can access it from here
351 // and record the name rather than the integer representation.
352 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
353 "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread",
354 "type", static_cast<int>(type
), "input_event_state",
355 InputEventStateToString(input_event_state
));
357 bool gesture_already_in_progress
= InputSignalsSuggestGestureInProgress(now
);
358 bool was_awaiting_touch_start_response
=
359 AnyThread().awaiting_touch_start_response
;
361 AnyThread().user_model
.DidStartProcessingInputEvent(type
, now
);
363 if (input_event_state
== InputEventState::EVENT_CONSUMED_BY_COMPOSITOR
)
364 AnyThread().user_model
.DidFinishProcessingInputEvent(now
);
368 case blink::WebInputEvent::TouchStart
:
369 AnyThread().awaiting_touch_start_response
= true;
372 case blink::WebInputEvent::TouchMove
:
373 // Observation of consecutive touchmoves is a strong signal that the
374 // page is consuming the touch sequence, in which case touchstart
375 // response prioritization is no longer necessary. Otherwise, the
376 // initial touchmove should preserve the touchstart response pending
378 if (AnyThread().awaiting_touch_start_response
&&
379 CompositorThreadOnly().last_input_type
==
380 blink::WebInputEvent::TouchMove
) {
381 AnyThread().awaiting_touch_start_response
= false;
385 case blink::WebInputEvent::Undefined
:
386 case blink::WebInputEvent::GestureTapDown
:
387 case blink::WebInputEvent::GestureShowPress
:
388 case blink::WebInputEvent::GestureFlingCancel
:
389 case blink::WebInputEvent::GestureScrollEnd
:
390 // With no observable effect, these meta events do not indicate a
391 // meaningful touchstart response and should not impact task priority.
395 AnyThread().awaiting_touch_start_response
= false;
400 // Avoid unnecessary policy updates, while a gesture is already in progress.
401 if (!gesture_already_in_progress
||
402 was_awaiting_touch_start_response
!=
403 AnyThread().awaiting_touch_start_response
) {
404 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE
);
406 CompositorThreadOnly().last_input_type
= type
;
409 void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
410 const blink::WebInputEvent
& web_input_event
) {
411 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
412 "RendererSchedulerImpl::DidHandleInputEventOnMainThread");
413 helper_
.CheckOnValidThread();
414 if (ShouldPrioritizeInputEvent(web_input_event
)) {
415 base::AutoLock
lock(any_thread_lock_
);
416 AnyThread().user_model
.DidFinishProcessingInputEvent(helper_
.Now());
420 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() {
421 helper_
.CheckOnValidThread();
422 if (helper_
.IsShutdown())
426 // The touchstart and main-thread gesture use cases indicate a strong
427 // likelihood of high-priority work in the near future.
428 UseCase use_case
= MainThreadOnly().current_use_case
;
429 return MainThreadOnly().touchstart_expected_soon
||
430 use_case
== UseCase::TOUCHSTART
||
431 use_case
== UseCase::MAIN_THREAD_GESTURE
;
434 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
435 helper_
.CheckOnValidThread();
436 if (helper_
.IsShutdown())
440 // We only yield if there's a urgent task to be run now, or we are expecting
441 // one soon (touch start).
442 // Note: even though the control queue has the highest priority we don't yield
443 // for it since these tasks are not user-provided work and they are only
444 // intended to run before the next task, not interrupt the tasks.
445 switch (MainThreadOnly().current_use_case
) {
447 return MainThreadOnly().touchstart_expected_soon
;
449 case UseCase::COMPOSITOR_GESTURE
:
450 return MainThreadOnly().touchstart_expected_soon
;
452 case UseCase::MAIN_THREAD_GESTURE
:
453 return !compositor_task_runner_
->IsQueueEmpty() ||
454 MainThreadOnly().touchstart_expected_soon
;
456 case UseCase::TOUCHSTART
:
459 case UseCase::LOADING
:
468 base::TimeTicks
RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
470 return idle_helper_
.CurrentIdleTaskDeadline();
473 void RendererSchedulerImpl::MaybeUpdatePolicy() {
474 helper_
.CheckOnValidThread();
475 if (policy_may_need_update_
.IsSet()) {
480 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
481 const tracked_objects::Location
& from_here
) {
482 // TODO(scheduler-dev): Check that this method isn't called from the main
484 any_thread_lock_
.AssertAcquired();
485 if (!policy_may_need_update_
.IsSet()) {
486 policy_may_need_update_
.SetWhileLocked(true);
487 control_task_runner_
->PostTask(from_here
, update_policy_closure_
);
491 void RendererSchedulerImpl::UpdatePolicy() {
492 base::AutoLock
lock(any_thread_lock_
);
493 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
496 void RendererSchedulerImpl::ForceUpdatePolicy() {
497 base::AutoLock
lock(any_thread_lock_
);
498 UpdatePolicyLocked(UpdateType::FORCE_UPDATE
);
501 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type
) {
502 helper_
.CheckOnValidThread();
503 any_thread_lock_
.AssertAcquired();
504 if (helper_
.IsShutdown())
507 base::TimeTicks now
= helper_
.Now();
508 policy_may_need_update_
.SetWhileLocked(false);
510 base::TimeDelta expected_use_case_duration
;
511 UseCase use_case
= ComputeCurrentUseCase(now
, &expected_use_case_duration
);
512 MainThreadOnly().current_use_case
= use_case
;
514 // TODO(alexclarke): We should wire up a signal from blink to let us know if
515 // there are any touch handlers registerd or not, and only call
516 // TouchStartExpectedSoon if there is at least one. NOTE a TouchStart will
517 // only actually get sent if there is a touch handler.
518 base::TimeDelta touchstart_expected_flag_valid_for_duration
;
519 bool touchstart_expected_soon
= AnyThread().user_model
.IsGestureExpectedSoon(
520 use_case
, now
, &touchstart_expected_flag_valid_for_duration
);
521 MainThreadOnly().touchstart_expected_soon
= touchstart_expected_soon
;
523 bool loading_tasks_seem_expensive
=
524 MainThreadOnly().loading_task_cost_estimator
.expected_task_duration() >
525 MainThreadOnly().expected_short_idle_period_duration
;
526 MainThreadOnly().loading_tasks_seem_expensive
= loading_tasks_seem_expensive
;
528 bool timer_tasks_seem_expensive
=
529 MainThreadOnly().timer_task_cost_estimator
.expected_task_duration() >
530 MainThreadOnly().expected_short_idle_period_duration
;
531 MainThreadOnly().timer_tasks_seem_expensive
= timer_tasks_seem_expensive
;
533 // The |new_policy_duration| is the minimum of |expected_use_case_duration|
534 // and |touchstart_expected_flag_valid_for_duration| unless one is zero in
535 // which case we choose the other.
536 base::TimeDelta new_policy_duration
= expected_use_case_duration
;
537 if (new_policy_duration
== base::TimeDelta() ||
538 (touchstart_expected_flag_valid_for_duration
> base::TimeDelta() &&
539 new_policy_duration
> touchstart_expected_flag_valid_for_duration
)) {
540 new_policy_duration
= touchstart_expected_flag_valid_for_duration
;
543 if (new_policy_duration
> base::TimeDelta()) {
544 MainThreadOnly().current_policy_expiration_time
= now
+ new_policy_duration
;
545 delayed_update_policy_runner_
.SetDeadline(FROM_HERE
, new_policy_duration
,
548 MainThreadOnly().current_policy_expiration_time
= base::TimeTicks();
552 bool block_expensive_tasks
= false;
554 case UseCase::COMPOSITOR_GESTURE
:
555 if (touchstart_expected_soon
) {
556 block_expensive_tasks
= true;
558 // What we really want to do is priorize loading tasks, but that doesn't
559 // seem to be safe. Instead we do that by proxy by deprioritizing
560 // compositor tasks. This should be safe since we've already gone to the
561 // pain of fixing ordering issues with them.
562 new_policy
.compositor_queue_priority
= TaskQueue::BEST_EFFORT_PRIORITY
;
566 case UseCase::MAIN_THREAD_GESTURE
:
567 new_policy
.compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
568 block_expensive_tasks
= true;
571 case UseCase::TOUCHSTART
:
572 new_policy
.compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
573 new_policy
.loading_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
574 new_policy
.timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
575 block_expensive_tasks
= true; // NOTE this is a nop due to the above.
579 if (touchstart_expected_soon
)
580 block_expensive_tasks
= true;
583 case UseCase::LOADING
:
584 new_policy
.loading_queue_priority
= TaskQueue::HIGH_PRIORITY
;
585 new_policy
.default_queue_priority
= TaskQueue::HIGH_PRIORITY
;
592 // Don't block expensive tasks unless we have actually seen something.
593 if (!MainThreadOnly().have_seen_a_begin_main_frame
)
594 block_expensive_tasks
= false;
596 if (block_expensive_tasks
&& loading_tasks_seem_expensive
)
597 new_policy
.loading_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
599 if ((block_expensive_tasks
&& timer_tasks_seem_expensive
) ||
600 MainThreadOnly().timer_queue_suspend_count
!= 0 ||
601 MainThreadOnly().timer_queue_suspended_when_backgrounded
) {
602 new_policy
.timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
605 // Tracing is done before the early out check, because it's quite possible we
606 // will otherwise miss this information in traces.
607 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
608 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
609 this, AsValueLocked(now
));
610 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "use_case",
612 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
613 "RendererScheduler.loading_tasks_seem_expensive",
614 MainThreadOnly().loading_tasks_seem_expensive
);
615 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
616 "RendererScheduler.timer_tasks_seem_expensive",
617 MainThreadOnly().timer_tasks_seem_expensive
);
619 if (update_type
== UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
&&
620 new_policy
== MainThreadOnly().current_policy
) {
624 compositor_task_runner_
->SetQueuePriority(
625 new_policy
.compositor_queue_priority
);
626 loading_task_runner_
->SetQueuePriority(new_policy
.loading_queue_priority
);
627 timer_task_runner_
->SetQueuePriority(new_policy
.timer_queue_priority
);
629 // TODO(alexclarke): We shouldn't have to prioritize the default queue, but it
630 // appears to be necessary since the order of loading tasks and IPCs (which
631 // are mostly dispatched on the default queue) need to be preserved.
632 helper_
.DefaultTaskRunner()->SetQueuePriority(
633 new_policy
.default_queue_priority
);
635 DCHECK(compositor_task_runner_
->IsQueueEnabled());
636 MainThreadOnly().current_policy
= new_policy
;
639 bool RendererSchedulerImpl::InputSignalsSuggestGestureInProgress(
640 base::TimeTicks now
) const {
641 base::TimeDelta unused_policy_duration
;
642 switch (ComputeCurrentUseCase(now
, &unused_policy_duration
)) {
643 case UseCase::COMPOSITOR_GESTURE
:
644 case UseCase::MAIN_THREAD_GESTURE
:
645 case UseCase::TOUCHSTART
:
654 RendererSchedulerImpl::UseCase
RendererSchedulerImpl::ComputeCurrentUseCase(
656 base::TimeDelta
* expected_use_case_duration
) const {
657 any_thread_lock_
.AssertAcquired();
658 // Above all else we want to be responsive to user input.
659 *expected_use_case_duration
=
660 AnyThread().user_model
.TimeLeftInUserGesture(now
);
661 if (*expected_use_case_duration
> base::TimeDelta()) {
662 // Has scrolling been fully established?
663 if (AnyThread().awaiting_touch_start_response
) {
664 // No, so arrange for compositor tasks to be run at the highest priority.
665 return UseCase::TOUCHSTART
;
667 // Yes scrolling has been established. If BeginMainFrame is on the critical
668 // path, compositor tasks need to be prioritized, otherwise now might be a
669 // good time to run potentially expensive work.
670 // TODO(skyostil): Consider removing in_idle_period_ and
671 // HadAnIdlePeriodRecently() unless we need them here.
672 if (AnyThread().begin_main_frame_on_critical_path
) {
673 return UseCase::MAIN_THREAD_GESTURE
;
675 return UseCase::COMPOSITOR_GESTURE
;
679 // TODO(alexclarke): return UseCase::LOADING if signals suggest the system is
680 // in the initial 1s of RAIL loading.
682 return UseCase::NONE
;
685 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
687 base::TimeDelta
* next_long_idle_period_delay_out
) {
688 helper_
.CheckOnValidThread();
691 if (MainThreadOnly().current_use_case
== UseCase::TOUCHSTART
) {
692 // Don't start a long idle task in touch start priority, try again when
693 // the policy is scheduled to end.
694 *next_long_idle_period_delay_out
=
695 MainThreadOnly().current_policy_expiration_time
- now
;
701 SchedulerHelper
* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
706 RendererSchedulerImpl::GetLoadingTaskCostEstimatorForTesting() {
707 return &MainThreadOnly().loading_task_cost_estimator
;
711 RendererSchedulerImpl::GetTimerTaskCostEstimatorForTesting() {
712 return &MainThreadOnly().timer_task_cost_estimator
;
715 void RendererSchedulerImpl::SuspendTimerQueue() {
716 MainThreadOnly().timer_queue_suspend_count
++;
718 DCHECK(!timer_task_runner_
->IsQueueEnabled());
721 void RendererSchedulerImpl::ResumeTimerQueue() {
722 MainThreadOnly().timer_queue_suspend_count
--;
723 DCHECK_GE(MainThreadOnly().timer_queue_suspend_count
, 0);
727 void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled(
729 // Note that this will only take effect for the next backgrounded signal.
730 MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled
= enabled
;
733 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
734 RendererSchedulerImpl::AsValue(base::TimeTicks optional_now
) const {
735 base::AutoLock
lock(any_thread_lock_
);
736 return AsValueLocked(optional_now
);
739 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
740 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now
) const {
741 helper_
.CheckOnValidThread();
742 any_thread_lock_
.AssertAcquired();
744 if (optional_now
.is_null())
745 optional_now
= helper_
.Now();
746 scoped_refptr
<base::trace_event::TracedValue
> state
=
747 new base::trace_event::TracedValue();
749 state
->SetString("current_use_case",
750 UseCaseToString(MainThreadOnly().current_use_case
));
751 state
->SetBoolean("loading_tasks_seem_expensive",
752 MainThreadOnly().loading_tasks_seem_expensive
);
753 state
->SetBoolean("timer_tasks_seem_expensive",
754 MainThreadOnly().timer_tasks_seem_expensive
);
755 state
->SetBoolean("touchstart_expected_soon",
756 MainThreadOnly().touchstart_expected_soon
);
757 state
->SetString("idle_period_state",
758 IdleHelper::IdlePeriodStateToString(
759 idle_helper_
.SchedulerIdlePeriodState()));
760 state
->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden
);
761 state
->SetBoolean("renderer_backgrounded",
762 MainThreadOnly().renderer_backgrounded
);
763 state
->SetBoolean("timer_queue_suspended_when_backgrounded",
764 MainThreadOnly().timer_queue_suspended_when_backgrounded
);
765 state
->SetInteger("timer_queue_suspend_count",
766 MainThreadOnly().timer_queue_suspend_count
);
767 state
->SetDouble("now", (optional_now
- base::TimeTicks()).InMillisecondsF());
769 "rails_loading_priority_deadline",
770 (AnyThread().rails_loading_priority_deadline
- base::TimeTicks())
772 state
->SetDouble("last_idle_period_end_time",
773 (AnyThread().last_idle_period_end_time
- base::TimeTicks())
775 state
->SetBoolean("awaiting_touch_start_response",
776 AnyThread().awaiting_touch_start_response
);
777 state
->SetBoolean("begin_main_frame_on_critical_path",
778 AnyThread().begin_main_frame_on_critical_path
);
779 state
->SetDouble("expected_loading_task_duration",
781 .loading_task_cost_estimator
.expected_task_duration()
783 state
->SetDouble("expected_timer_task_duration",
785 .timer_task_cost_estimator
.expected_task_duration()
787 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
789 "expected_short_idle_period_duration",
790 MainThreadOnly().expected_short_idle_period_duration
.InMillisecondsF());
792 "estimated_next_frame_begin",
793 (MainThreadOnly().estimated_next_frame_begin
- base::TimeTicks())
795 state
->SetBoolean("in_idle_period", AnyThread().in_idle_period
);
796 AnyThread().user_model
.AsValueInto(state
.get());
801 void RendererSchedulerImpl::OnIdlePeriodStarted() {
802 base::AutoLock
lock(any_thread_lock_
);
803 AnyThread().in_idle_period
= true;
804 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
807 void RendererSchedulerImpl::OnIdlePeriodEnded() {
808 base::AutoLock
lock(any_thread_lock_
);
809 AnyThread().last_idle_period_end_time
= helper_
.Now();
810 AnyThread().in_idle_period
= false;
811 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
814 void RendererSchedulerImpl::OnPageLoadStarted() {
815 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
816 "RendererSchedulerImpl::OnPageLoadStarted");
817 base::AutoLock
lock(any_thread_lock_
);
818 AnyThread().rails_loading_priority_deadline
=
819 helper_
.Now() + base::TimeDelta::FromMilliseconds(
820 kRailsInitialLoadingPrioritizationMillis
);
821 ResetForNavigationLocked();
824 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now
) const {
825 return (now
- AnyThread().last_idle_period_end_time
) <=
826 base::TimeDelta::FromMilliseconds(
827 kIdlePeriodStarvationThresholdMillis
);
830 void RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded() {
831 DCHECK(MainThreadOnly().renderer_backgrounded
);
832 if (MainThreadOnly().timer_queue_suspended_when_backgrounded
)
835 MainThreadOnly().timer_queue_suspended_when_backgrounded
= true;
839 void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() {
840 DCHECK(!MainThreadOnly().renderer_backgrounded
);
841 if (!MainThreadOnly().timer_queue_suspended_when_backgrounded
)
844 MainThreadOnly().timer_queue_suspended_when_backgrounded
= false;
848 void RendererSchedulerImpl::ResetForNavigationLocked() {
849 helper_
.CheckOnValidThread();
850 any_thread_lock_
.AssertAcquired();
851 MainThreadOnly().loading_task_cost_estimator
.Clear();
852 MainThreadOnly().timer_task_cost_estimator
.Clear();
853 MainThreadOnly().short_idle_period_duration
.Clear();
854 // Make sure that we don't initially assume there is no idle time.
855 MainThreadOnly().short_idle_period_duration
.InsertSample(
856 cc::BeginFrameArgs::DefaultInterval());
857 AnyThread().user_model
.Reset();
858 MainThreadOnly().have_seen_a_begin_main_frame
= false;
859 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
862 } // namespace scheduler