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 kTimerTaskEstimationSampleCount
= 4 * 60;
19 const double kTimerTaskEstimationPercentile
= 80;
20 const int kShortIdlePeriodDurationSampleCount
= 10;
21 const double kShortIdlePeriodDurationPercentile
= 20;
24 RendererSchedulerImpl::RendererSchedulerImpl(
25 scoped_refptr
<SchedulerTaskRunnerDelegate
> main_task_runner
)
26 : helper_(main_task_runner
,
28 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
29 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")),
30 idle_helper_(&helper_
,
33 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
34 "RendererSchedulerIdlePeriod",
36 control_task_runner_(helper_
.ControlTaskRunner()),
37 compositor_task_runner_(
38 helper_
.NewTaskQueue(TaskQueue::Spec("compositor_tq")
39 .SetShouldMonitorQuiescence(true))),
41 helper_
.NewTaskQueue(TaskQueue::Spec("loading_tq")
42 .SetShouldMonitorQuiescence(true))),
44 helper_
.NewTaskQueue(TaskQueue::Spec("timer_tq")
45 .SetShouldMonitorQuiescence(true))),
46 delayed_update_policy_runner_(
47 base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
48 base::Unretained(this)),
49 helper_
.ControlTaskRunner()),
50 policy_may_need_update_(&any_thread_lock_
),
52 update_policy_closure_
= base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
53 weak_factory_
.GetWeakPtr());
54 end_renderer_hidden_idle_period_closure_
.Reset(base::Bind(
55 &RendererSchedulerImpl::EndIdlePeriod
, weak_factory_
.GetWeakPtr()));
57 suspend_timers_when_backgrounded_closure_
.Reset(
58 base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded
,
59 weak_factory_
.GetWeakPtr()));
61 timer_task_runner_
->AddTaskObserver(
62 &MainThreadOnly().timer_task_cost_estimator_
);
64 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
65 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
69 RendererSchedulerImpl::~RendererSchedulerImpl() {
70 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
71 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
73 timer_task_runner_
->RemoveTaskObserver(
74 &MainThreadOnly().timer_task_cost_estimator_
);
75 // Ensure the renderer scheduler was shut down explicitly, because otherwise
76 // we could end up having stale pointers to the Blink heap which has been
77 // terminated by this point.
78 DCHECK(MainThreadOnly().was_shutdown_
);
81 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly()
82 : timer_task_cost_estimator_(kTimerTaskEstimationSampleCount
,
83 kTimerTaskEstimationPercentile
),
84 short_idle_period_duration_(kShortIdlePeriodDurationSampleCount
),
85 current_policy_(Policy::NORMAL
),
86 timer_queue_suspend_count_(0),
87 renderer_hidden_(false),
88 renderer_backgrounded_(false),
89 timer_queue_suspension_when_backgrounded_enabled_(false),
90 timer_queue_suspended_when_backgrounded_(false),
91 was_shutdown_(false) {}
93 RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
95 RendererSchedulerImpl::AnyThread::AnyThread()
96 : pending_main_thread_input_event_count_(0),
97 awaiting_touch_start_response_(false),
98 in_idle_period_(false),
99 begin_main_frame_on_critical_path_(false),
100 timer_tasks_seem_expensive_(false) {}
102 RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
103 : last_input_type_(blink::WebInputEvent::Undefined
) {
106 RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {
109 void RendererSchedulerImpl::Shutdown() {
111 MainThreadOnly().was_shutdown_
= true;
114 scoped_refptr
<TaskQueue
> RendererSchedulerImpl::DefaultTaskRunner() {
115 return helper_
.DefaultTaskRunner();
118 scoped_refptr
<base::SingleThreadTaskRunner
>
119 RendererSchedulerImpl::CompositorTaskRunner() {
120 helper_
.CheckOnValidThread();
121 return compositor_task_runner_
;
124 scoped_refptr
<SingleThreadIdleTaskRunner
>
125 RendererSchedulerImpl::IdleTaskRunner() {
126 return idle_helper_
.IdleTaskRunner();
129 scoped_refptr
<base::SingleThreadTaskRunner
>
130 RendererSchedulerImpl::LoadingTaskRunner() {
131 helper_
.CheckOnValidThread();
132 return loading_task_runner_
;
135 scoped_refptr
<TaskQueue
> RendererSchedulerImpl::TimerTaskRunner() {
136 helper_
.CheckOnValidThread();
137 return timer_task_runner_
;
140 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
141 return idle_helper_
.CanExceedIdleDeadlineIfRequired();
144 void RendererSchedulerImpl::AddTaskObserver(
145 base::MessageLoop::TaskObserver
* task_observer
) {
146 helper_
.AddTaskObserver(task_observer
);
149 void RendererSchedulerImpl::RemoveTaskObserver(
150 base::MessageLoop::TaskObserver
* task_observer
) {
151 helper_
.RemoveTaskObserver(task_observer
);
154 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs
& args
) {
155 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
156 "RendererSchedulerImpl::WillBeginFrame", "args", args
.AsValue());
157 helper_
.CheckOnValidThread();
158 if (helper_
.IsShutdown())
162 MainThreadOnly().estimated_next_frame_begin_
=
163 args
.frame_time
+ args
.interval
;
165 base::AutoLock
lock(any_thread_lock_
);
166 AnyThread().begin_main_frame_on_critical_path_
= args
.on_critical_path
;
170 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
171 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
172 "RendererSchedulerImpl::DidCommitFrameToCompositor");
173 helper_
.CheckOnValidThread();
174 if (helper_
.IsShutdown())
177 base::TimeTicks
now(helper_
.Now());
178 if (now
< MainThreadOnly().estimated_next_frame_begin_
) {
179 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
180 // the next pending delayed tasks (as currently done in for long idle times)
181 idle_helper_
.StartIdlePeriod(
182 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD
, now
,
183 MainThreadOnly().estimated_next_frame_begin_
);
184 MainThreadOnly().short_idle_period_duration_
.InsertSample(
185 MainThreadOnly().estimated_next_frame_begin_
- now
);
186 MainThreadOnly().expected_short_idle_period_duration_
=
187 MainThreadOnly().short_idle_period_duration_
.Percentile(
188 kShortIdlePeriodDurationPercentile
);
192 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
193 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
194 "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
195 helper_
.CheckOnValidThread();
196 if (helper_
.IsShutdown())
199 idle_helper_
.EnableLongIdlePeriod();
202 void RendererSchedulerImpl::OnRendererHidden() {
203 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
204 "RendererSchedulerImpl::OnRendererHidden");
205 helper_
.CheckOnValidThread();
206 if (helper_
.IsShutdown() || MainThreadOnly().renderer_hidden_
)
209 idle_helper_
.EnableLongIdlePeriod();
211 // Ensure that we stop running idle tasks after a few seconds of being hidden.
212 end_renderer_hidden_idle_period_closure_
.Cancel();
213 base::TimeDelta end_idle_when_hidden_delay
=
214 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis
);
215 control_task_runner_
->PostDelayedTask(
216 FROM_HERE
, end_renderer_hidden_idle_period_closure_
.callback(),
217 end_idle_when_hidden_delay
);
218 MainThreadOnly().renderer_hidden_
= true;
220 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
221 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
222 this, AsValue(helper_
.Now()));
225 void RendererSchedulerImpl::OnRendererVisible() {
226 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
227 "RendererSchedulerImpl::OnRendererVisible");
228 helper_
.CheckOnValidThread();
229 if (helper_
.IsShutdown() || !MainThreadOnly().renderer_hidden_
)
232 end_renderer_hidden_idle_period_closure_
.Cancel();
233 MainThreadOnly().renderer_hidden_
= false;
236 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
237 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
238 this, AsValue(helper_
.Now()));
241 void RendererSchedulerImpl::OnRendererBackgrounded() {
242 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
243 "RendererSchedulerImpl::OnRendererBackgrounded");
244 helper_
.CheckOnValidThread();
245 if (helper_
.IsShutdown() || MainThreadOnly().renderer_backgrounded_
)
248 MainThreadOnly().renderer_backgrounded_
= true;
249 if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled_
)
252 suspend_timers_when_backgrounded_closure_
.Cancel();
253 base::TimeDelta suspend_timers_when_backgrounded_delay
=
254 base::TimeDelta::FromMilliseconds(
255 kSuspendTimersWhenBackgroundedDelayMillis
);
256 control_task_runner_
->PostDelayedTask(
257 FROM_HERE
, suspend_timers_when_backgrounded_closure_
.callback(),
258 suspend_timers_when_backgrounded_delay
);
261 void RendererSchedulerImpl::OnRendererForegrounded() {
262 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
263 "RendererSchedulerImpl::OnRendererForegrounded");
264 helper_
.CheckOnValidThread();
265 if (helper_
.IsShutdown() || !MainThreadOnly().renderer_backgrounded_
)
268 MainThreadOnly().renderer_backgrounded_
= false;
269 suspend_timers_when_backgrounded_closure_
.Cancel();
270 ResumeTimerQueueWhenForegrounded();
273 void RendererSchedulerImpl::EndIdlePeriod() {
274 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
275 "RendererSchedulerImpl::EndIdlePeriod");
276 helper_
.CheckOnValidThread();
277 idle_helper_
.EndIdlePeriod();
281 bool RendererSchedulerImpl::ShouldPrioritizeInputEvent(
282 const blink::WebInputEvent
& web_input_event
) {
283 // We regard MouseMove events with the left mouse button down as a signal
284 // that the user is doing something requiring a smooth frame rate.
285 if (web_input_event
.type
== blink::WebInputEvent::MouseMove
&&
286 (web_input_event
.modifiers
& blink::WebInputEvent::LeftButtonDown
)) {
289 // Ignore all other mouse events because they probably don't signal user
290 // interaction needing a smooth framerate. NOTE isMouseEventType returns false
291 // for mouse wheel events, hence we regard them as user input.
292 // Ignore keyboard events because it doesn't really make sense to enter
293 // compositor priority for them.
294 if (blink::WebInputEvent::isMouseEventType(web_input_event
.type
) ||
295 blink::WebInputEvent::isKeyboardEventType(web_input_event
.type
)) {
301 void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread(
302 const blink::WebInputEvent
& web_input_event
,
303 InputEventState event_state
) {
304 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
305 "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread");
306 if (!ShouldPrioritizeInputEvent(web_input_event
))
309 UpdateForInputEventOnCompositorThread(web_input_event
.type
, event_state
);
312 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
313 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
314 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread");
315 UpdateForInputEventOnCompositorThread(
316 blink::WebInputEvent::Undefined
,
317 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR
);
320 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread(
321 blink::WebInputEvent::Type type
,
322 InputEventState input_event_state
) {
323 base::AutoLock
lock(any_thread_lock_
);
324 base::TimeTicks now
= helper_
.Now();
325 bool was_in_compositor_priority
= InputSignalsSuggestCompositorPriority(now
);
326 bool was_awaiting_touch_start_response
=
327 AnyThread().awaiting_touch_start_response_
;
331 case blink::WebInputEvent::TouchStart
:
332 AnyThread().awaiting_touch_start_response_
= true;
335 case blink::WebInputEvent::TouchMove
:
336 // Observation of consecutive touchmoves is a strong signal that the
337 // page is consuming the touch sequence, in which case touchstart
338 // response prioritization is no longer necessary. Otherwise, the
339 // initial touchmove should preserve the touchstart response pending
341 if (AnyThread().awaiting_touch_start_response_
&&
342 CompositorThreadOnly().last_input_type_
==
343 blink::WebInputEvent::TouchMove
) {
344 AnyThread().awaiting_touch_start_response_
= false;
348 case blink::WebInputEvent::Undefined
:
349 case blink::WebInputEvent::GestureTapDown
:
350 case blink::WebInputEvent::GestureShowPress
:
351 case blink::WebInputEvent::GestureFlingCancel
:
352 case blink::WebInputEvent::GestureScrollEnd
:
353 // With no observable effect, these meta events do not indicate a
354 // meaningful touchstart response and should not impact task priority.
358 AnyThread().awaiting_touch_start_response_
= false;
363 // Avoid unnecessary policy updates, while in compositor priority.
364 if (!was_in_compositor_priority
||
365 was_awaiting_touch_start_response
!=
366 AnyThread().awaiting_touch_start_response_
) {
367 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE
);
369 AnyThread().last_input_signal_time_
= now
;
370 CompositorThreadOnly().last_input_type_
= type
;
372 if (input_event_state
== InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD
)
373 AnyThread().pending_main_thread_input_event_count_
++;
376 void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
377 const blink::WebInputEvent
& web_input_event
) {
378 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
379 "RendererSchedulerImpl::DidHandleInputEventOnMainThread");
380 helper_
.CheckOnValidThread();
381 if (ShouldPrioritizeInputEvent(web_input_event
)) {
382 base::AutoLock
lock(any_thread_lock_
);
383 AnyThread().last_input_signal_time_
= helper_
.Now();
384 if (AnyThread().pending_main_thread_input_event_count_
> 0)
385 AnyThread().pending_main_thread_input_event_count_
--;
389 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() {
390 helper_
.CheckOnValidThread();
391 if (helper_
.IsShutdown())
395 // The touchstart and compositor policies indicate a strong likelihood of
396 // high-priority work in the near future.
397 return MainThreadOnly().current_policy_
== Policy::COMPOSITOR_PRIORITY
||
398 MainThreadOnly().current_policy_
==
399 Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
||
400 MainThreadOnly().current_policy_
== Policy::TOUCHSTART_PRIORITY
;
403 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
404 helper_
.CheckOnValidThread();
405 if (helper_
.IsShutdown())
409 // We only yield if we are in the compositor priority and there is compositor
410 // work outstanding, or if we are in the touchstart response priority.
411 // Note: even though the control queue is higher priority we don't yield for
412 // it since these tasks are not user-provided work and they are only intended
413 // to run before the next task, not interrupt the tasks.
414 switch (MainThreadOnly().current_policy_
) {
418 case Policy::COMPOSITOR_PRIORITY
:
419 return !compositor_task_runner_
->IsQueueEmpty();
421 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
422 return !compositor_task_runner_
->IsQueueEmpty();
424 case Policy::TOUCHSTART_PRIORITY
:
427 case Policy::LOADING_PRIORITY
:
436 base::TimeTicks
RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
438 return idle_helper_
.CurrentIdleTaskDeadline();
441 void RendererSchedulerImpl::MaybeUpdatePolicy() {
442 helper_
.CheckOnValidThread();
443 if (policy_may_need_update_
.IsSet()) {
448 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
449 const tracked_objects::Location
& from_here
) {
450 // TODO(scheduler-dev): Check that this method isn't called from the main
452 any_thread_lock_
.AssertAcquired();
453 if (!policy_may_need_update_
.IsSet()) {
454 policy_may_need_update_
.SetWhileLocked(true);
455 control_task_runner_
->PostTask(from_here
, update_policy_closure_
);
459 void RendererSchedulerImpl::UpdatePolicy() {
460 base::AutoLock
lock(any_thread_lock_
);
461 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
464 void RendererSchedulerImpl::ForceUpdatePolicy() {
465 base::AutoLock
lock(any_thread_lock_
);
466 UpdatePolicyLocked(UpdateType::FORCE_UPDATE
);
469 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type
) {
470 helper_
.CheckOnValidThread();
471 any_thread_lock_
.AssertAcquired();
472 if (helper_
.IsShutdown())
475 base::TimeTicks now
= helper_
.Now();
476 policy_may_need_update_
.SetWhileLocked(false);
478 AnyThread().timer_tasks_seem_expensive_
=
479 MainThreadOnly().expected_short_idle_period_duration_
>
481 MainThreadOnly().timer_task_cost_estimator_
.expected_task_duration() >
482 MainThreadOnly().expected_short_idle_period_duration_
;
484 base::TimeDelta new_policy_duration
;
485 Policy new_policy
= ComputeNewPolicy(now
, &new_policy_duration
);
486 if (new_policy_duration
> base::TimeDelta()) {
487 MainThreadOnly().current_policy_expiration_time_
=
488 now
+ new_policy_duration
;
489 delayed_update_policy_runner_
.SetDeadline(FROM_HERE
, new_policy_duration
,
492 MainThreadOnly().current_policy_expiration_time_
= base::TimeTicks();
495 if (update_type
== UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
&&
496 new_policy
== MainThreadOnly().current_policy_
)
499 TaskQueue::QueuePriority compositor_queue_priority
=
500 TaskQueue::NORMAL_PRIORITY
;
501 TaskQueue::QueuePriority loading_queue_priority
= TaskQueue::NORMAL_PRIORITY
;
502 TaskQueue::QueuePriority timer_queue_priority
= TaskQueue::NORMAL_PRIORITY
;
504 if (MainThreadOnly().timer_queue_suspend_count_
!= 0 ||
505 MainThreadOnly().timer_queue_suspended_when_backgrounded_
) {
506 timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
509 switch (new_policy
) {
510 case Policy::COMPOSITOR_PRIORITY
:
511 compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
513 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
514 compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
515 loading_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
516 timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
518 case Policy::TOUCHSTART_PRIORITY
:
519 compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
520 loading_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
521 timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
525 case Policy::LOADING_PRIORITY
:
526 // We prioritize loading tasks by deprioritizing compositing and timers.
527 compositor_queue_priority
= TaskQueue::BEST_EFFORT_PRIORITY
;
528 timer_queue_priority
= TaskQueue::BEST_EFFORT_PRIORITY
;
529 // TODO(alexclarke): See if we can safely mark the loading task queue as
536 compositor_task_runner_
->SetQueuePriority(compositor_queue_priority
);
537 loading_task_runner_
->SetQueuePriority(loading_queue_priority
);
538 timer_task_runner_
->SetQueuePriority(timer_queue_priority
);
540 DCHECK(compositor_task_runner_
->IsQueueEnabled());
541 if (new_policy
!= Policy::TOUCHSTART_PRIORITY
&&
542 new_policy
!= Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
) {
543 DCHECK(loading_task_runner_
->IsQueueEnabled());
545 MainThreadOnly().current_policy_
= new_policy
;
547 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
548 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
549 this, AsValueLocked(now
));
550 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
551 "RendererScheduler.policy", MainThreadOnly().current_policy_
);
552 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
553 "RendererScheduler.timer_tasks_seem_expensive",
554 AnyThread().timer_tasks_seem_expensive_
);
557 bool RendererSchedulerImpl::InputSignalsSuggestCompositorPriority(
558 base::TimeTicks now
) const {
559 base::TimeDelta unused_policy_duration
;
560 switch (ComputeNewPolicy(now
, &unused_policy_duration
)) {
561 case Policy::TOUCHSTART_PRIORITY
:
562 case Policy::COMPOSITOR_PRIORITY
:
563 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
572 RendererSchedulerImpl::Policy
RendererSchedulerImpl::ComputeNewPolicy(
574 base::TimeDelta
* new_policy_duration
) const {
575 any_thread_lock_
.AssertAcquired();
576 // Above all else we want to be responsive to user input.
577 *new_policy_duration
= TimeLeftInInputEscalatedPolicy(now
);
578 if (*new_policy_duration
> base::TimeDelta()) {
579 if (AnyThread().awaiting_touch_start_response_
)
580 return Policy::TOUCHSTART_PRIORITY
;
581 // If BeginMainFrame is on the critical path, we want to try and prevent
582 // timers and loading tasks from running if we think they might be
584 // TODO(skyostil): Consider removing in_idle_period_ and
585 // HadAnIdlePeriodRecently() unless we need them here.
586 if (AnyThread().timer_tasks_seem_expensive_
&&
587 AnyThread().begin_main_frame_on_critical_path_
) {
588 return Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
;
590 return Policy::COMPOSITOR_PRIORITY
;
593 if (AnyThread().rails_loading_priority_deadline_
> now
) {
594 *new_policy_duration
= AnyThread().rails_loading_priority_deadline_
- now
;
595 return Policy::LOADING_PRIORITY
;
598 return Policy::NORMAL
;
601 base::TimeDelta
RendererSchedulerImpl::TimeLeftInInputEscalatedPolicy(
602 base::TimeTicks now
) const {
603 any_thread_lock_
.AssertAcquired();
605 base::TimeDelta escalated_priority_duration
=
606 base::TimeDelta::FromMilliseconds(kPriorityEscalationAfterInputMillis
);
608 // If the input event is still pending, go into input prioritized policy
609 // and check again later.
610 if (AnyThread().pending_main_thread_input_event_count_
> 0)
611 return escalated_priority_duration
;
612 if (AnyThread().last_input_signal_time_
.is_null() ||
613 AnyThread().last_input_signal_time_
+ escalated_priority_duration
< now
) {
614 return base::TimeDelta();
616 return AnyThread().last_input_signal_time_
+ escalated_priority_duration
-
620 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
622 base::TimeDelta
* next_long_idle_period_delay_out
) {
623 helper_
.CheckOnValidThread();
626 if (MainThreadOnly().current_policy_
== Policy::TOUCHSTART_PRIORITY
) {
627 // Don't start a long idle task in touch start priority, try again when
628 // the policy is scheduled to end.
629 *next_long_idle_period_delay_out
=
630 MainThreadOnly().current_policy_expiration_time_
- now
;
636 SchedulerHelper
* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
640 void RendererSchedulerImpl::SuspendTimerQueue() {
641 MainThreadOnly().timer_queue_suspend_count_
++;
643 DCHECK(!timer_task_runner_
->IsQueueEnabled());
646 void RendererSchedulerImpl::ResumeTimerQueue() {
647 MainThreadOnly().timer_queue_suspend_count_
--;
648 DCHECK_GE(MainThreadOnly().timer_queue_suspend_count_
, 0);
652 void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled(
654 // Note that this will only take effect for the next backgrounded signal.
655 MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled_
= enabled
;
659 const char* RendererSchedulerImpl::PolicyToString(Policy policy
) {
663 case Policy::COMPOSITOR_PRIORITY
:
665 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
666 return "compositor_critical_path";
667 case Policy::TOUCHSTART_PRIORITY
:
669 case Policy::LOADING_PRIORITY
:
677 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
678 RendererSchedulerImpl::AsValue(base::TimeTicks optional_now
) const {
679 base::AutoLock
lock(any_thread_lock_
);
680 return AsValueLocked(optional_now
);
683 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
684 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now
) const {
685 helper_
.CheckOnValidThread();
686 any_thread_lock_
.AssertAcquired();
688 if (optional_now
.is_null())
689 optional_now
= helper_
.Now();
690 scoped_refptr
<base::trace_event::TracedValue
> state
=
691 new base::trace_event::TracedValue();
693 state
->SetString("current_policy",
694 PolicyToString(MainThreadOnly().current_policy_
));
695 state
->SetString("idle_period_state",
696 IdleHelper::IdlePeriodStateToString(
697 idle_helper_
.SchedulerIdlePeriodState()));
698 state
->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden_
);
699 state
->SetBoolean("renderer_backgrounded",
700 MainThreadOnly().renderer_backgrounded_
);
701 state
->SetBoolean("timer_queue_suspended_when_backgrounded",
702 MainThreadOnly().timer_queue_suspended_when_backgrounded_
);
703 state
->SetInteger("timer_queue_suspend_count",
704 MainThreadOnly().timer_queue_suspend_count_
);
705 state
->SetDouble("now", (optional_now
- base::TimeTicks()).InMillisecondsF());
706 state
->SetDouble("last_input_signal_time",
707 (AnyThread().last_input_signal_time_
- base::TimeTicks())
709 state
->SetDouble("rails_loading_priority_deadline",
710 (AnyThread().rails_loading_priority_deadline_
-
711 base::TimeTicks()).InMillisecondsF());
712 state
->SetDouble("last_idle_period_end_time",
713 (AnyThread().last_idle_period_end_time_
- base::TimeTicks())
715 state
->SetInteger("pending_main_thread_input_event_count",
716 AnyThread().pending_main_thread_input_event_count_
);
717 state
->SetBoolean("awaiting_touch_start_response",
718 AnyThread().awaiting_touch_start_response_
);
719 state
->SetBoolean("begin_main_frame_on_critical_path",
720 AnyThread().begin_main_frame_on_critical_path_
);
721 state
->SetDouble("expected_timer_task_duration",
723 .timer_task_cost_estimator_
.expected_task_duration()
725 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
727 "expected_short_idle_period_duration",
728 MainThreadOnly().expected_short_idle_period_duration_
.InMillisecondsF());
729 state
->SetBoolean("timer_tasks_seem_expensive",
730 AnyThread().timer_tasks_seem_expensive_
);
731 state
->SetDouble("estimated_next_frame_begin",
732 (MainThreadOnly().estimated_next_frame_begin_
-
733 base::TimeTicks()).InMillisecondsF());
734 state
->SetBoolean("in_idle_period", AnyThread().in_idle_period_
);
739 void RendererSchedulerImpl::OnIdlePeriodStarted() {
740 base::AutoLock
lock(any_thread_lock_
);
741 AnyThread().in_idle_period_
= true;
742 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
745 void RendererSchedulerImpl::OnIdlePeriodEnded() {
746 base::AutoLock
lock(any_thread_lock_
);
747 AnyThread().last_idle_period_end_time_
= helper_
.Now();
748 AnyThread().in_idle_period_
= false;
749 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
752 void RendererSchedulerImpl::OnPageLoadStarted() {
753 base::AutoLock
lock(any_thread_lock_
);
754 AnyThread().rails_loading_priority_deadline_
=
755 helper_
.Now() + base::TimeDelta::FromMilliseconds(
756 kRailsInitialLoadingPrioritizationMillis
);
757 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
760 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now
) const {
761 return (now
- AnyThread().last_idle_period_end_time_
) <=
762 base::TimeDelta::FromMilliseconds(
763 kIdlePeriodStarvationThresholdMillis
);
766 void RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded() {
767 DCHECK(MainThreadOnly().renderer_backgrounded_
);
768 if (MainThreadOnly().timer_queue_suspended_when_backgrounded_
)
771 MainThreadOnly().timer_queue_suspended_when_backgrounded_
= true;
775 void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() {
776 DCHECK(!MainThreadOnly().renderer_backgrounded_
);
777 if (!MainThreadOnly().timer_queue_suspended_when_backgrounded_
)
780 MainThreadOnly().timer_queue_suspended_when_backgrounded_
= false;
784 } // namespace scheduler