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 timer_task_runner_
->AddTaskObserver(
58 &MainThreadOnly().timer_task_cost_estimator_
);
60 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
61 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
65 RendererSchedulerImpl::~RendererSchedulerImpl() {
66 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
67 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
69 timer_task_runner_
->RemoveTaskObserver(
70 &MainThreadOnly().timer_task_cost_estimator_
);
71 // Ensure the renderer scheduler was shut down explicitly, because otherwise
72 // we could end up having stale pointers to the Blink heap which has been
73 // terminated by this point.
74 DCHECK(MainThreadOnly().was_shutdown_
);
77 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly()
78 : timer_task_cost_estimator_(kTimerTaskEstimationSampleCount
,
79 kTimerTaskEstimationPercentile
),
80 short_idle_period_duration_(kShortIdlePeriodDurationSampleCount
),
81 current_policy_(Policy::NORMAL
),
82 timer_queue_suspend_count_(0),
83 renderer_hidden_(false),
84 was_shutdown_(false) {}
86 RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
88 RendererSchedulerImpl::AnyThread::AnyThread()
89 : pending_main_thread_input_event_count_(0),
90 awaiting_touch_start_response_(false),
91 in_idle_period_(false),
92 begin_main_frame_on_critical_path_(false),
93 timer_tasks_seem_expensive_(false) {}
95 RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
96 : last_input_type_(blink::WebInputEvent::Undefined
) {
99 RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {
102 void RendererSchedulerImpl::Shutdown() {
104 MainThreadOnly().was_shutdown_
= true;
107 scoped_refptr
<TaskQueue
> RendererSchedulerImpl::DefaultTaskRunner() {
108 return helper_
.DefaultTaskRunner();
111 scoped_refptr
<base::SingleThreadTaskRunner
>
112 RendererSchedulerImpl::CompositorTaskRunner() {
113 helper_
.CheckOnValidThread();
114 return compositor_task_runner_
;
117 scoped_refptr
<SingleThreadIdleTaskRunner
>
118 RendererSchedulerImpl::IdleTaskRunner() {
119 return idle_helper_
.IdleTaskRunner();
122 scoped_refptr
<base::SingleThreadTaskRunner
>
123 RendererSchedulerImpl::LoadingTaskRunner() {
124 helper_
.CheckOnValidThread();
125 return loading_task_runner_
;
128 scoped_refptr
<TaskQueue
> RendererSchedulerImpl::TimerTaskRunner() {
129 helper_
.CheckOnValidThread();
130 return timer_task_runner_
;
133 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
134 return idle_helper_
.CanExceedIdleDeadlineIfRequired();
137 void RendererSchedulerImpl::AddTaskObserver(
138 base::MessageLoop::TaskObserver
* task_observer
) {
139 helper_
.AddTaskObserver(task_observer
);
142 void RendererSchedulerImpl::RemoveTaskObserver(
143 base::MessageLoop::TaskObserver
* task_observer
) {
144 helper_
.RemoveTaskObserver(task_observer
);
147 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs
& args
) {
148 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
149 "RendererSchedulerImpl::WillBeginFrame", "args", args
.AsValue());
150 helper_
.CheckOnValidThread();
151 if (helper_
.IsShutdown())
155 MainThreadOnly().estimated_next_frame_begin_
=
156 args
.frame_time
+ args
.interval
;
158 base::AutoLock
lock(any_thread_lock_
);
159 AnyThread().begin_main_frame_on_critical_path_
= args
.on_critical_path
;
163 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
164 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
165 "RendererSchedulerImpl::DidCommitFrameToCompositor");
166 helper_
.CheckOnValidThread();
167 if (helper_
.IsShutdown())
170 base::TimeTicks
now(helper_
.Now());
171 if (now
< MainThreadOnly().estimated_next_frame_begin_
) {
172 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
173 // the next pending delayed tasks (as currently done in for long idle times)
174 idle_helper_
.StartIdlePeriod(
175 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD
, now
,
176 MainThreadOnly().estimated_next_frame_begin_
);
177 MainThreadOnly().short_idle_period_duration_
.InsertSample(
178 MainThreadOnly().estimated_next_frame_begin_
- now
);
179 MainThreadOnly().expected_short_idle_period_duration_
=
180 MainThreadOnly().short_idle_period_duration_
.Percentile(
181 kShortIdlePeriodDurationPercentile
);
185 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
186 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
187 "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
188 helper_
.CheckOnValidThread();
189 if (helper_
.IsShutdown())
192 idle_helper_
.EnableLongIdlePeriod();
195 void RendererSchedulerImpl::OnRendererHidden() {
196 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
197 "RendererSchedulerImpl::OnRendererHidden");
198 helper_
.CheckOnValidThread();
199 if (helper_
.IsShutdown() || MainThreadOnly().renderer_hidden_
)
202 idle_helper_
.EnableLongIdlePeriod();
204 // Ensure that we stop running idle tasks after a few seconds of being hidden.
205 end_renderer_hidden_idle_period_closure_
.Cancel();
206 base::TimeDelta end_idle_when_hidden_delay
=
207 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis
);
208 control_task_runner_
->PostDelayedTask(
209 FROM_HERE
, end_renderer_hidden_idle_period_closure_
.callback(),
210 end_idle_when_hidden_delay
);
211 MainThreadOnly().renderer_hidden_
= true;
213 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
214 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
215 this, AsValue(helper_
.Now()));
218 void RendererSchedulerImpl::OnRendererVisible() {
219 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
220 "RendererSchedulerImpl::OnRendererVisible");
221 helper_
.CheckOnValidThread();
222 if (helper_
.IsShutdown() || !MainThreadOnly().renderer_hidden_
)
225 end_renderer_hidden_idle_period_closure_
.Cancel();
226 MainThreadOnly().renderer_hidden_
= false;
229 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
230 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
231 this, AsValue(helper_
.Now()));
234 void RendererSchedulerImpl::EndIdlePeriod() {
235 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
236 "RendererSchedulerImpl::EndIdlePeriod");
237 helper_
.CheckOnValidThread();
238 idle_helper_
.EndIdlePeriod();
242 bool RendererSchedulerImpl::ShouldPrioritizeInputEvent(
243 const blink::WebInputEvent
& web_input_event
) {
244 // We regard MouseMove events with the left mouse button down as a signal
245 // that the user is doing something requiring a smooth frame rate.
246 if (web_input_event
.type
== blink::WebInputEvent::MouseMove
&&
247 (web_input_event
.modifiers
& blink::WebInputEvent::LeftButtonDown
)) {
250 // Ignore all other mouse events because they probably don't signal user
251 // interaction needing a smooth framerate. NOTE isMouseEventType returns false
252 // for mouse wheel events, hence we regard them as user input.
253 // Ignore keyboard events because it doesn't really make sense to enter
254 // compositor priority for them.
255 if (blink::WebInputEvent::isMouseEventType(web_input_event
.type
) ||
256 blink::WebInputEvent::isKeyboardEventType(web_input_event
.type
)) {
262 void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread(
263 const blink::WebInputEvent
& web_input_event
,
264 InputEventState event_state
) {
265 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
266 "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread");
267 if (!ShouldPrioritizeInputEvent(web_input_event
))
270 UpdateForInputEventOnCompositorThread(web_input_event
.type
, event_state
);
273 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
274 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
275 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread");
276 UpdateForInputEventOnCompositorThread(
277 blink::WebInputEvent::Undefined
,
278 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR
);
281 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread(
282 blink::WebInputEvent::Type type
,
283 InputEventState input_event_state
) {
284 base::AutoLock
lock(any_thread_lock_
);
285 base::TimeTicks now
= helper_
.Now();
286 bool was_in_compositor_priority
= InputSignalsSuggestCompositorPriority(now
);
287 bool was_awaiting_touch_start_response
=
288 AnyThread().awaiting_touch_start_response_
;
292 case blink::WebInputEvent::TouchStart
:
293 AnyThread().awaiting_touch_start_response_
= true;
296 case blink::WebInputEvent::TouchMove
:
297 // Observation of consecutive touchmoves is a strong signal that the
298 // page is consuming the touch sequence, in which case touchstart
299 // response prioritization is no longer necessary. Otherwise, the
300 // initial touchmove should preserve the touchstart response pending
302 if (AnyThread().awaiting_touch_start_response_
&&
303 CompositorThreadOnly().last_input_type_
==
304 blink::WebInputEvent::TouchMove
) {
305 AnyThread().awaiting_touch_start_response_
= false;
309 case blink::WebInputEvent::GestureTapDown
:
310 case blink::WebInputEvent::GestureShowPress
:
311 case blink::WebInputEvent::GestureFlingCancel
:
312 case blink::WebInputEvent::GestureScrollEnd
:
313 // With no observable effect, these meta events do not indicate a
314 // meaningful touchstart response and should not impact task priority.
318 AnyThread().awaiting_touch_start_response_
= false;
323 // Avoid unnecessary policy updates, while in compositor priority.
324 if (!was_in_compositor_priority
||
325 was_awaiting_touch_start_response
!=
326 AnyThread().awaiting_touch_start_response_
) {
327 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE
);
329 AnyThread().last_input_signal_time_
= now
;
330 CompositorThreadOnly().last_input_type_
= type
;
332 if (input_event_state
== InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD
)
333 AnyThread().pending_main_thread_input_event_count_
++;
336 void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
337 const blink::WebInputEvent
& web_input_event
) {
338 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
339 "RendererSchedulerImpl::DidHandleInputEventOnMainThread");
340 helper_
.CheckOnValidThread();
341 if (ShouldPrioritizeInputEvent(web_input_event
)) {
342 base::AutoLock
lock(any_thread_lock_
);
343 AnyThread().last_input_signal_time_
= helper_
.Now();
344 if (AnyThread().pending_main_thread_input_event_count_
> 0)
345 AnyThread().pending_main_thread_input_event_count_
--;
349 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() {
350 helper_
.CheckOnValidThread();
351 if (helper_
.IsShutdown())
355 // The touchstart and compositor policies indicate a strong likelihood of
356 // high-priority work in the near future.
357 return MainThreadOnly().current_policy_
== Policy::COMPOSITOR_PRIORITY
||
358 MainThreadOnly().current_policy_
==
359 Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
||
360 MainThreadOnly().current_policy_
== Policy::TOUCHSTART_PRIORITY
;
363 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
364 helper_
.CheckOnValidThread();
365 if (helper_
.IsShutdown())
369 // We only yield if we are in the compositor priority and there is compositor
370 // work outstanding, or if we are in the touchstart response priority.
371 // Note: even though the control queue is higher priority we don't yield for
372 // it since these tasks are not user-provided work and they are only intended
373 // to run before the next task, not interrupt the tasks.
374 switch (MainThreadOnly().current_policy_
) {
378 case Policy::COMPOSITOR_PRIORITY
:
379 return !compositor_task_runner_
->IsQueueEmpty();
381 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
382 return !compositor_task_runner_
->IsQueueEmpty();
384 case Policy::TOUCHSTART_PRIORITY
:
387 case Policy::LOADING_PRIORITY
:
396 base::TimeTicks
RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
398 return idle_helper_
.CurrentIdleTaskDeadline();
401 void RendererSchedulerImpl::MaybeUpdatePolicy() {
402 helper_
.CheckOnValidThread();
403 if (policy_may_need_update_
.IsSet()) {
408 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
409 const tracked_objects::Location
& from_here
) {
410 // TODO(scheduler-dev): Check that this method isn't called from the main
412 any_thread_lock_
.AssertAcquired();
413 if (!policy_may_need_update_
.IsSet()) {
414 policy_may_need_update_
.SetWhileLocked(true);
415 control_task_runner_
->PostTask(from_here
, update_policy_closure_
);
419 void RendererSchedulerImpl::UpdatePolicy() {
420 base::AutoLock
lock(any_thread_lock_
);
421 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
424 void RendererSchedulerImpl::ForceUpdatePolicy() {
425 base::AutoLock
lock(any_thread_lock_
);
426 UpdatePolicyLocked(UpdateType::FORCE_UPDATE
);
429 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type
) {
430 helper_
.CheckOnValidThread();
431 any_thread_lock_
.AssertAcquired();
432 if (helper_
.IsShutdown())
435 base::TimeTicks now
= helper_
.Now();
436 policy_may_need_update_
.SetWhileLocked(false);
438 AnyThread().timer_tasks_seem_expensive_
=
439 MainThreadOnly().expected_short_idle_period_duration_
>
441 MainThreadOnly().timer_task_cost_estimator_
.expected_task_duration() >
442 MainThreadOnly().expected_short_idle_period_duration_
;
444 base::TimeDelta new_policy_duration
;
445 Policy new_policy
= ComputeNewPolicy(now
, &new_policy_duration
);
446 if (new_policy_duration
> base::TimeDelta()) {
447 MainThreadOnly().current_policy_expiration_time_
=
448 now
+ new_policy_duration
;
449 delayed_update_policy_runner_
.SetDeadline(FROM_HERE
, new_policy_duration
,
452 MainThreadOnly().current_policy_expiration_time_
= base::TimeTicks();
455 if (update_type
== UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
&&
456 new_policy
== MainThreadOnly().current_policy_
)
459 TaskQueue::QueuePriority compositor_queue_priority
=
460 TaskQueue::NORMAL_PRIORITY
;
461 TaskQueue::QueuePriority loading_queue_priority
= TaskQueue::NORMAL_PRIORITY
;
462 TaskQueue::QueuePriority timer_queue_priority
=
463 MainThreadOnly().timer_queue_suspend_count_
!= 0
464 ? TaskQueue::DISABLED_PRIORITY
465 : TaskQueue::NORMAL_PRIORITY
;
467 switch (new_policy
) {
468 case Policy::COMPOSITOR_PRIORITY
:
469 compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
471 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
472 compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
473 loading_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
474 timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
476 case Policy::TOUCHSTART_PRIORITY
:
477 compositor_queue_priority
= TaskQueue::HIGH_PRIORITY
;
478 loading_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
479 timer_queue_priority
= TaskQueue::DISABLED_PRIORITY
;
483 case Policy::LOADING_PRIORITY
:
484 // We prioritize loading tasks by deprioritizing compositing and timers.
485 compositor_queue_priority
= TaskQueue::BEST_EFFORT_PRIORITY
;
486 timer_queue_priority
= TaskQueue::BEST_EFFORT_PRIORITY
;
487 // TODO(alexclarke): See if we can safely mark the loading task queue as
494 compositor_task_runner_
->SetQueuePriority(compositor_queue_priority
);
495 loading_task_runner_
->SetQueuePriority(loading_queue_priority
);
496 timer_task_runner_
->SetQueuePriority(timer_queue_priority
);
498 DCHECK(compositor_task_runner_
->IsQueueEnabled());
499 if (new_policy
!= Policy::TOUCHSTART_PRIORITY
&&
500 new_policy
!= Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
) {
501 DCHECK(loading_task_runner_
->IsQueueEnabled());
503 MainThreadOnly().current_policy_
= new_policy
;
505 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
506 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
507 this, AsValueLocked(now
));
508 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
509 "RendererScheduler.policy", MainThreadOnly().current_policy_
);
510 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
511 "RendererScheduler.timer_tasks_seem_expensive",
512 AnyThread().timer_tasks_seem_expensive_
);
515 bool RendererSchedulerImpl::InputSignalsSuggestCompositorPriority(
516 base::TimeTicks now
) const {
517 base::TimeDelta unused_policy_duration
;
518 switch (ComputeNewPolicy(now
, &unused_policy_duration
)) {
519 case Policy::TOUCHSTART_PRIORITY
:
520 case Policy::COMPOSITOR_PRIORITY
:
521 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
530 RendererSchedulerImpl::Policy
RendererSchedulerImpl::ComputeNewPolicy(
532 base::TimeDelta
* new_policy_duration
) const {
533 any_thread_lock_
.AssertAcquired();
534 // Above all else we want to be responsive to user input.
535 *new_policy_duration
= TimeLeftInInputEscalatedPolicy(now
);
536 if (*new_policy_duration
> base::TimeDelta()) {
537 if (AnyThread().awaiting_touch_start_response_
)
538 return Policy::TOUCHSTART_PRIORITY
;
539 // If BeginMainFrame is on the critical path, we want to try and prevent
540 // timers and loading tasks from running if we think they might be
542 // TODO(skyostil): Consider removing in_idle_period_ and
543 // HadAnIdlePeriodRecently() unless we need them here.
544 if (AnyThread().timer_tasks_seem_expensive_
&&
545 AnyThread().begin_main_frame_on_critical_path_
) {
546 return Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
;
548 return Policy::COMPOSITOR_PRIORITY
;
551 if (AnyThread().rails_loading_priority_deadline_
> now
) {
552 *new_policy_duration
= AnyThread().rails_loading_priority_deadline_
- now
;
553 return Policy::LOADING_PRIORITY
;
556 return Policy::NORMAL
;
559 base::TimeDelta
RendererSchedulerImpl::TimeLeftInInputEscalatedPolicy(
560 base::TimeTicks now
) const {
561 any_thread_lock_
.AssertAcquired();
563 base::TimeDelta escalated_priority_duration
=
564 base::TimeDelta::FromMilliseconds(kPriorityEscalationAfterInputMillis
);
566 // If the input event is still pending, go into input prioritized policy
567 // and check again later.
568 if (AnyThread().pending_main_thread_input_event_count_
> 0)
569 return escalated_priority_duration
;
570 if (AnyThread().last_input_signal_time_
.is_null() ||
571 AnyThread().last_input_signal_time_
+ escalated_priority_duration
< now
) {
572 return base::TimeDelta();
574 return AnyThread().last_input_signal_time_
+ escalated_priority_duration
-
578 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
580 base::TimeDelta
* next_long_idle_period_delay_out
) {
581 helper_
.CheckOnValidThread();
584 if (MainThreadOnly().current_policy_
== Policy::TOUCHSTART_PRIORITY
) {
585 // Don't start a long idle task in touch start priority, try again when
586 // the policy is scheduled to end.
587 *next_long_idle_period_delay_out
=
588 MainThreadOnly().current_policy_expiration_time_
- now
;
594 SchedulerHelper
* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
598 void RendererSchedulerImpl::SuspendTimerQueue() {
599 MainThreadOnly().timer_queue_suspend_count_
++;
601 DCHECK(!timer_task_runner_
->IsQueueEnabled());
604 void RendererSchedulerImpl::ResumeTimerQueue() {
605 MainThreadOnly().timer_queue_suspend_count_
--;
606 DCHECK_GE(MainThreadOnly().timer_queue_suspend_count_
, 0);
611 const char* RendererSchedulerImpl::PolicyToString(Policy policy
) {
615 case Policy::COMPOSITOR_PRIORITY
:
617 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
618 return "compositor_critical_path";
619 case Policy::TOUCHSTART_PRIORITY
:
621 case Policy::LOADING_PRIORITY
:
629 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
630 RendererSchedulerImpl::AsValue(base::TimeTicks optional_now
) const {
631 base::AutoLock
lock(any_thread_lock_
);
632 return AsValueLocked(optional_now
);
635 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
636 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now
) const {
637 helper_
.CheckOnValidThread();
638 any_thread_lock_
.AssertAcquired();
640 if (optional_now
.is_null())
641 optional_now
= helper_
.Now();
642 scoped_refptr
<base::trace_event::TracedValue
> state
=
643 new base::trace_event::TracedValue();
645 state
->SetString("current_policy",
646 PolicyToString(MainThreadOnly().current_policy_
));
647 state
->SetString("idle_period_state",
648 IdleHelper::IdlePeriodStateToString(
649 idle_helper_
.SchedulerIdlePeriodState()));
650 state
->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden_
);
651 state
->SetDouble("now", (optional_now
- base::TimeTicks()).InMillisecondsF());
652 state
->SetDouble("last_input_signal_time",
653 (AnyThread().last_input_signal_time_
- base::TimeTicks())
655 state
->SetDouble("rails_loading_priority_deadline",
656 (AnyThread().rails_loading_priority_deadline_
-
657 base::TimeTicks()).InMillisecondsF());
658 state
->SetDouble("last_idle_period_end_time",
659 (AnyThread().last_idle_period_end_time_
- base::TimeTicks())
661 state
->SetInteger("pending_main_thread_input_event_count",
662 AnyThread().pending_main_thread_input_event_count_
);
663 state
->SetBoolean("awaiting_touch_start_response",
664 AnyThread().awaiting_touch_start_response_
);
665 state
->SetBoolean("begin_main_frame_on_critical_path",
666 AnyThread().begin_main_frame_on_critical_path_
);
667 state
->SetDouble("expected_timer_task_duration",
669 .timer_task_cost_estimator_
.expected_task_duration()
671 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
673 "expected_short_idle_period_duration",
674 MainThreadOnly().expected_short_idle_period_duration_
.InMillisecondsF());
675 state
->SetBoolean("timer_tasks_seem_expensive",
676 AnyThread().timer_tasks_seem_expensive_
);
677 state
->SetDouble("estimated_next_frame_begin",
678 (MainThreadOnly().estimated_next_frame_begin_
-
679 base::TimeTicks()).InMillisecondsF());
680 state
->SetBoolean("in_idle_period", AnyThread().in_idle_period_
);
685 void RendererSchedulerImpl::OnIdlePeriodStarted() {
686 base::AutoLock
lock(any_thread_lock_
);
687 AnyThread().in_idle_period_
= true;
688 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
691 void RendererSchedulerImpl::OnIdlePeriodEnded() {
692 base::AutoLock
lock(any_thread_lock_
);
693 AnyThread().last_idle_period_end_time_
= helper_
.Now();
694 AnyThread().in_idle_period_
= false;
695 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
698 void RendererSchedulerImpl::OnPageLoadStarted() {
699 base::AutoLock
lock(any_thread_lock_
);
700 AnyThread().rails_loading_priority_deadline_
=
701 helper_
.Now() + base::TimeDelta::FromMilliseconds(
702 kRailsInitialLoadingPrioritizationMillis
);
703 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
706 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now
) const {
707 return (now
- AnyThread().last_idle_period_end_time_
) <=
708 base::TimeDelta::FromMilliseconds(
709 kIdlePeriodStarvationThresholdMillis
);
712 } // namespace scheduler