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/nestable_single_thread_task_runner.h"
13 #include "components/scheduler/child/prioritizing_task_queue_selector.h"
17 RendererSchedulerImpl::RendererSchedulerImpl(
18 scoped_refptr
<NestableSingleThreadTaskRunner
> main_task_runner
)
19 : helper_(main_task_runner
,
21 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
22 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"),
24 idle_helper_(&helper_
,
28 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
29 "RendererSchedulerIdlePeriod",
31 control_task_runner_(helper_
.ControlTaskRunner()),
32 compositor_task_runner_(
33 helper_
.TaskRunnerForQueue(COMPOSITOR_TASK_QUEUE
)),
34 loading_task_runner_(helper_
.TaskRunnerForQueue(LOADING_TASK_QUEUE
)),
35 timer_task_runner_(helper_
.TaskRunnerForQueue(TIMER_TASK_QUEUE
)),
36 delayed_update_policy_runner_(
37 base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
38 base::Unretained(this)),
39 helper_
.ControlTaskRunner()),
40 policy_may_need_update_(&any_thread_lock_
),
42 update_policy_closure_
= base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
43 weak_factory_
.GetWeakPtr());
44 end_renderer_hidden_idle_period_closure_
.Reset(base::Bind(
45 &RendererSchedulerImpl::EndIdlePeriod
, weak_factory_
.GetWeakPtr()));
47 for (size_t i
= SchedulerHelper::TASK_QUEUE_COUNT
; i
< TASK_QUEUE_COUNT
;
49 helper_
.SetQueueName(i
, TaskQueueIdToString(static_cast<QueueId
>(i
)));
51 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
52 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
56 RendererSchedulerImpl::~RendererSchedulerImpl() {
57 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
58 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
60 // Ensure the renderer scheduler was shut down explicitly, because otherwise
61 // we could end up having stale pointers to the Blink heap which has been
62 // terminated by this point.
63 DCHECK(MainThreadOnly().was_shutdown_
);
66 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly()
67 : current_policy_(Policy::NORMAL
),
68 timer_queue_suspend_count_(0),
69 renderer_hidden_(false),
70 was_shutdown_(false) {
73 RendererSchedulerImpl::AnyThread::AnyThread()
74 : pending_main_thread_input_event_count_(0),
75 awaiting_touch_start_response_(false),
76 in_idle_period_(false),
77 begin_main_frame_on_critical_path_(false) {
80 RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
81 : last_input_type_(blink::WebInputEvent::Undefined
) {
84 RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {
87 void RendererSchedulerImpl::Shutdown() {
89 MainThreadOnly().was_shutdown_
= true;
92 scoped_refptr
<base::SingleThreadTaskRunner
>
93 RendererSchedulerImpl::DefaultTaskRunner() {
94 return helper_
.DefaultTaskRunner();
97 scoped_refptr
<base::SingleThreadTaskRunner
>
98 RendererSchedulerImpl::CompositorTaskRunner() {
99 helper_
.CheckOnValidThread();
100 return compositor_task_runner_
;
103 scoped_refptr
<SingleThreadIdleTaskRunner
>
104 RendererSchedulerImpl::IdleTaskRunner() {
105 return idle_helper_
.IdleTaskRunner();
108 scoped_refptr
<base::SingleThreadTaskRunner
>
109 RendererSchedulerImpl::LoadingTaskRunner() {
110 helper_
.CheckOnValidThread();
111 return loading_task_runner_
;
114 scoped_refptr
<base::SingleThreadTaskRunner
>
115 RendererSchedulerImpl::TimerTaskRunner() {
116 helper_
.CheckOnValidThread();
117 return timer_task_runner_
;
120 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
121 return idle_helper_
.CanExceedIdleDeadlineIfRequired();
124 void RendererSchedulerImpl::AddTaskObserver(
125 base::MessageLoop::TaskObserver
* task_observer
) {
126 helper_
.AddTaskObserver(task_observer
);
129 void RendererSchedulerImpl::RemoveTaskObserver(
130 base::MessageLoop::TaskObserver
* task_observer
) {
131 helper_
.RemoveTaskObserver(task_observer
);
134 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs
& args
) {
135 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
136 "RendererSchedulerImpl::WillBeginFrame", "args", args
.AsValue());
137 helper_
.CheckOnValidThread();
138 if (helper_
.IsShutdown())
142 MainThreadOnly().estimated_next_frame_begin_
=
143 args
.frame_time
+ args
.interval
;
145 base::AutoLock
lock(any_thread_lock_
);
146 AnyThread().begin_main_frame_on_critical_path_
= args
.on_critical_path
;
150 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
151 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
152 "RendererSchedulerImpl::DidCommitFrameToCompositor");
153 helper_
.CheckOnValidThread();
154 if (helper_
.IsShutdown())
157 base::TimeTicks
now(helper_
.Now());
158 if (now
< MainThreadOnly().estimated_next_frame_begin_
) {
159 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
160 // the next pending delayed tasks (as currently done in for long idle times)
161 idle_helper_
.StartIdlePeriod(
162 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD
, now
,
163 MainThreadOnly().estimated_next_frame_begin_
);
167 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
168 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
169 "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
170 helper_
.CheckOnValidThread();
171 if (helper_
.IsShutdown())
174 idle_helper_
.EnableLongIdlePeriod();
177 void RendererSchedulerImpl::OnRendererHidden() {
178 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
179 "RendererSchedulerImpl::OnRendererHidden");
180 helper_
.CheckOnValidThread();
181 if (helper_
.IsShutdown() || MainThreadOnly().renderer_hidden_
)
184 idle_helper_
.EnableLongIdlePeriod();
186 // Ensure that we stop running idle tasks after a few seconds of being hidden.
187 end_renderer_hidden_idle_period_closure_
.Cancel();
188 base::TimeDelta end_idle_when_hidden_delay
=
189 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis
);
190 control_task_runner_
->PostDelayedTask(
191 FROM_HERE
, end_renderer_hidden_idle_period_closure_
.callback(),
192 end_idle_when_hidden_delay
);
193 MainThreadOnly().renderer_hidden_
= true;
195 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
196 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
197 this, AsValue(helper_
.Now()));
200 void RendererSchedulerImpl::OnRendererVisible() {
201 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
202 "RendererSchedulerImpl::OnRendererVisible");
203 helper_
.CheckOnValidThread();
204 if (helper_
.IsShutdown() || !MainThreadOnly().renderer_hidden_
)
207 end_renderer_hidden_idle_period_closure_
.Cancel();
208 MainThreadOnly().renderer_hidden_
= false;
211 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
212 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
213 this, AsValue(helper_
.Now()));
216 void RendererSchedulerImpl::EndIdlePeriod() {
217 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
218 "RendererSchedulerImpl::EndIdlePeriod");
219 helper_
.CheckOnValidThread();
220 idle_helper_
.EndIdlePeriod();
224 bool RendererSchedulerImpl::ShouldPrioritizeInputEvent(
225 const blink::WebInputEvent
& web_input_event
) {
226 // We regard MouseMove events with the left mouse button down as a signal
227 // that the user is doing something requiring a smooth frame rate.
228 if (web_input_event
.type
== blink::WebInputEvent::MouseMove
&&
229 (web_input_event
.modifiers
& blink::WebInputEvent::LeftButtonDown
)) {
232 // Ignore all other mouse events because they probably don't signal user
233 // interaction needing a smooth framerate. NOTE isMouseEventType returns false
234 // for mouse wheel events, hence we regard them as user input.
235 // Ignore keyboard events because it doesn't really make sense to enter
236 // compositor priority for them.
237 if (blink::WebInputEvent::isMouseEventType(web_input_event
.type
) ||
238 blink::WebInputEvent::isKeyboardEventType(web_input_event
.type
)) {
244 void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread(
245 const blink::WebInputEvent
& web_input_event
,
246 InputEventState event_state
) {
247 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
248 "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread");
249 if (!ShouldPrioritizeInputEvent(web_input_event
))
252 UpdateForInputEventOnCompositorThread(web_input_event
.type
, event_state
);
255 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
256 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
257 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread");
258 UpdateForInputEventOnCompositorThread(
259 blink::WebInputEvent::Undefined
,
260 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR
);
263 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread(
264 blink::WebInputEvent::Type type
,
265 InputEventState input_event_state
) {
266 base::AutoLock
lock(any_thread_lock_
);
267 base::TimeTicks now
= helper_
.Now();
268 bool was_in_compositor_priority
= InputSignalsSuggestCompositorPriority(now
);
269 bool was_awaiting_touch_start_response
=
270 AnyThread().awaiting_touch_start_response_
;
274 case blink::WebInputEvent::TouchStart
:
275 AnyThread().awaiting_touch_start_response_
= true;
278 case blink::WebInputEvent::TouchMove
:
279 // Observation of consecutive touchmoves is a strong signal that the
280 // page is consuming the touch sequence, in which case touchstart
281 // response prioritization is no longer necessary. Otherwise, the
282 // initial touchmove should preserve the touchstart response pending
284 if (AnyThread().awaiting_touch_start_response_
&&
285 CompositorThreadOnly().last_input_type_
==
286 blink::WebInputEvent::TouchMove
) {
287 AnyThread().awaiting_touch_start_response_
= false;
291 case blink::WebInputEvent::GestureTapDown
:
292 case blink::WebInputEvent::GestureShowPress
:
293 case blink::WebInputEvent::GestureFlingCancel
:
294 case blink::WebInputEvent::GestureScrollEnd
:
295 // With no observable effect, these meta events do not indicate a
296 // meaningful touchstart response and should not impact task priority.
300 AnyThread().awaiting_touch_start_response_
= false;
305 // Avoid unnecessary policy updates, while in compositor priority.
306 if (!was_in_compositor_priority
||
307 was_awaiting_touch_start_response
!=
308 AnyThread().awaiting_touch_start_response_
) {
309 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE
);
311 AnyThread().last_input_signal_time_
= now
;
312 CompositorThreadOnly().last_input_type_
= type
;
314 if (input_event_state
== InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD
)
315 AnyThread().pending_main_thread_input_event_count_
++;
318 void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
319 const blink::WebInputEvent
& web_input_event
) {
320 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
321 "RendererSchedulerImpl::DidHandleInputEventOnMainThread");
322 helper_
.CheckOnValidThread();
323 if (ShouldPrioritizeInputEvent(web_input_event
)) {
324 base::AutoLock
lock(any_thread_lock_
);
325 AnyThread().last_input_signal_time_
= helper_
.Now();
326 if (AnyThread().pending_main_thread_input_event_count_
> 0)
327 AnyThread().pending_main_thread_input_event_count_
--;
331 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() {
332 helper_
.CheckOnValidThread();
333 if (helper_
.IsShutdown())
337 // The touchstart and compositor policies indicate a strong likelihood of
338 // high-priority work in the near future.
339 return MainThreadOnly().current_policy_
== Policy::COMPOSITOR_PRIORITY
||
340 MainThreadOnly().current_policy_
==
341 Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
||
342 MainThreadOnly().current_policy_
== Policy::TOUCHSTART_PRIORITY
;
345 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
346 helper_
.CheckOnValidThread();
347 if (helper_
.IsShutdown())
351 // We only yield if we are in the compositor priority and there is compositor
352 // work outstanding, or if we are in the touchstart response priority.
353 // Note: even though the control queue is higher priority we don't yield for
354 // it since these tasks are not user-provided work and they are only intended
355 // to run before the next task, not interrupt the tasks.
356 switch (MainThreadOnly().current_policy_
) {
360 case Policy::COMPOSITOR_PRIORITY
:
361 return !helper_
.IsQueueEmpty(COMPOSITOR_TASK_QUEUE
);
363 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
364 return !helper_
.IsQueueEmpty(COMPOSITOR_TASK_QUEUE
);
366 case Policy::TOUCHSTART_PRIORITY
:
369 case Policy::LOADING_PRIORITY
:
378 base::TimeTicks
RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
380 return idle_helper_
.CurrentIdleTaskDeadline();
383 void RendererSchedulerImpl::MaybeUpdatePolicy() {
384 helper_
.CheckOnValidThread();
385 if (policy_may_need_update_
.IsSet()) {
390 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
391 const tracked_objects::Location
& from_here
) {
392 // TODO(scheduler-dev): Check that this method isn't called from the main
394 any_thread_lock_
.AssertAcquired();
395 if (!policy_may_need_update_
.IsSet()) {
396 policy_may_need_update_
.SetWhileLocked(true);
397 control_task_runner_
->PostTask(from_here
, update_policy_closure_
);
401 void RendererSchedulerImpl::UpdatePolicy() {
402 base::AutoLock
lock(any_thread_lock_
);
403 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
406 void RendererSchedulerImpl::ForceUpdatePolicy() {
407 base::AutoLock
lock(any_thread_lock_
);
408 UpdatePolicyLocked(UpdateType::FORCE_UPDATE
);
411 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type
) {
412 helper_
.CheckOnValidThread();
413 any_thread_lock_
.AssertAcquired();
414 if (helper_
.IsShutdown())
417 base::TimeTicks now
= helper_
.Now();
418 policy_may_need_update_
.SetWhileLocked(false);
420 base::TimeDelta new_policy_duration
;
421 Policy new_policy
= ComputeNewPolicy(now
, &new_policy_duration
);
422 if (new_policy_duration
> base::TimeDelta()) {
423 MainThreadOnly().current_policy_expiration_time_
=
424 now
+ new_policy_duration
;
425 delayed_update_policy_runner_
.SetDeadline(FROM_HERE
, new_policy_duration
,
428 MainThreadOnly().current_policy_expiration_time_
= base::TimeTicks();
431 if (update_type
== UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
&&
432 new_policy
== MainThreadOnly().current_policy_
)
435 bool policy_disables_timer_queue
= false;
436 PrioritizingTaskQueueSelector::QueuePriority timer_queue_priority
=
437 PrioritizingTaskQueueSelector::NORMAL_PRIORITY
;
439 switch (new_policy
) {
440 case Policy::COMPOSITOR_PRIORITY
:
441 helper_
.SetQueuePriority(COMPOSITOR_TASK_QUEUE
,
442 PrioritizingTaskQueueSelector::HIGH_PRIORITY
);
443 helper_
.SetQueuePriority(LOADING_TASK_QUEUE
,
444 PrioritizingTaskQueueSelector::NORMAL_PRIORITY
);
446 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
447 helper_
.SetQueuePriority(COMPOSITOR_TASK_QUEUE
,
448 PrioritizingTaskQueueSelector::HIGH_PRIORITY
);
449 // TODO(scheduler-dev): Add a task priority between HIGH and BEST_EFFORT
450 // that still has some guarantee of running.
451 helper_
.SetQueuePriority(
453 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY
);
454 helper_
.DisableQueue(LOADING_TASK_QUEUE
);
455 policy_disables_timer_queue
= true;
457 case Policy::TOUCHSTART_PRIORITY
:
458 helper_
.SetQueuePriority(COMPOSITOR_TASK_QUEUE
,
459 PrioritizingTaskQueueSelector::HIGH_PRIORITY
);
460 helper_
.DisableQueue(LOADING_TASK_QUEUE
);
461 policy_disables_timer_queue
= true;
464 helper_
.SetQueuePriority(COMPOSITOR_TASK_QUEUE
,
465 PrioritizingTaskQueueSelector::NORMAL_PRIORITY
);
466 helper_
.SetQueuePriority(LOADING_TASK_QUEUE
,
467 PrioritizingTaskQueueSelector::NORMAL_PRIORITY
);
469 case Policy::LOADING_PRIORITY
:
470 // We prioritize loading tasks by deprioritizing compositing and timers.
471 helper_
.SetQueuePriority(LOADING_TASK_QUEUE
,
472 PrioritizingTaskQueueSelector::NORMAL_PRIORITY
);
473 helper_
.SetQueuePriority(
474 COMPOSITOR_TASK_QUEUE
,
475 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY
);
476 timer_queue_priority
=
477 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY
;
478 // TODO(alexclarke): See if we can safely mark the loading task queue as
484 if (MainThreadOnly().timer_queue_suspend_count_
!= 0 ||
485 policy_disables_timer_queue
) {
486 helper_
.DisableQueue(TIMER_TASK_QUEUE
);
488 helper_
.SetQueuePriority(TIMER_TASK_QUEUE
, timer_queue_priority
);
490 DCHECK(helper_
.IsQueueEnabled(COMPOSITOR_TASK_QUEUE
));
491 if (new_policy
!= Policy::TOUCHSTART_PRIORITY
&&
492 new_policy
!= Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
) {
493 DCHECK(helper_
.IsQueueEnabled(LOADING_TASK_QUEUE
));
495 MainThreadOnly().current_policy_
= new_policy
;
497 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
498 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
499 this, AsValueLocked(now
));
500 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
501 "RendererScheduler.policy", MainThreadOnly().current_policy_
);
504 bool RendererSchedulerImpl::InputSignalsSuggestCompositorPriority(
505 base::TimeTicks now
) const {
506 base::TimeDelta unused_policy_duration
;
507 switch (ComputeNewPolicy(now
, &unused_policy_duration
)) {
508 case Policy::TOUCHSTART_PRIORITY
:
509 case Policy::COMPOSITOR_PRIORITY
:
510 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
519 RendererSchedulerImpl::Policy
RendererSchedulerImpl::ComputeNewPolicy(
521 base::TimeDelta
* new_policy_duration
) const {
522 any_thread_lock_
.AssertAcquired();
523 // Above all else we want to be responsive to user input.
524 *new_policy_duration
= TimeLeftInInputEscalatedPolicy(now
);
525 if (*new_policy_duration
> base::TimeDelta()) {
526 if (AnyThread().awaiting_touch_start_response_
)
527 return Policy::TOUCHSTART_PRIORITY
;
528 // If BeginMainFrame is on the critical path, we want to try and prevent
529 // timers and loading tasks from running shortly before BeginMainFrame is
530 // due to be posted from the compositor, because they can delay
531 // BeginMainFrame's execution. We do this by limiting execution of timers to
532 // idle periods, provided there has been at least one idle period recently.
534 // TODO(alexclarke): It's a shame in_idle_period_,
535 // begin_main_frame_on_critical_path_ and last_idle_period_end_time_ are in
536 // the AnyThread struct. Find a way to migrate them to MainThreadOnly.
537 if (!AnyThread().in_idle_period_
&&
538 AnyThread().begin_main_frame_on_critical_path_
&&
539 HadAnIdlePeriodRecently(now
)) {
540 return Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
;
542 return Policy::COMPOSITOR_PRIORITY
;
545 if (AnyThread().rails_loading_priority_deadline_
> now
) {
546 *new_policy_duration
= AnyThread().rails_loading_priority_deadline_
- now
;
547 return Policy::LOADING_PRIORITY
;
550 return Policy::NORMAL
;
553 base::TimeDelta
RendererSchedulerImpl::TimeLeftInInputEscalatedPolicy(
554 base::TimeTicks now
) const {
555 any_thread_lock_
.AssertAcquired();
557 base::TimeDelta escalated_priority_duration
=
558 base::TimeDelta::FromMilliseconds(kPriorityEscalationAfterInputMillis
);
560 // If the input event is still pending, go into input prioritized policy
561 // and check again later.
562 if (AnyThread().pending_main_thread_input_event_count_
> 0)
563 return escalated_priority_duration
;
564 if (AnyThread().last_input_signal_time_
.is_null() ||
565 AnyThread().last_input_signal_time_
+ escalated_priority_duration
< now
) {
566 return base::TimeDelta();
568 return AnyThread().last_input_signal_time_
+ escalated_priority_duration
-
572 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
574 base::TimeDelta
* next_long_idle_period_delay_out
) {
575 helper_
.CheckOnValidThread();
578 if (MainThreadOnly().current_policy_
== Policy::TOUCHSTART_PRIORITY
) {
579 // Don't start a long idle task in touch start priority, try again when
580 // the policy is scheduled to end.
581 *next_long_idle_period_delay_out
=
582 MainThreadOnly().current_policy_expiration_time_
- now
;
588 SchedulerHelper
* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
592 void RendererSchedulerImpl::SuspendTimerQueue() {
593 MainThreadOnly().timer_queue_suspend_count_
++;
595 DCHECK(!helper_
.IsQueueEnabled(TIMER_TASK_QUEUE
));
598 void RendererSchedulerImpl::ResumeTimerQueue() {
599 MainThreadOnly().timer_queue_suspend_count_
--;
600 DCHECK_GE(MainThreadOnly().timer_queue_suspend_count_
, 0);
605 const char* RendererSchedulerImpl::TaskQueueIdToString(QueueId queue_id
) {
607 case IDLE_TASK_QUEUE
:
609 case COMPOSITOR_TASK_QUEUE
:
610 return "compositor_tq";
611 case LOADING_TASK_QUEUE
:
613 case TIMER_TASK_QUEUE
:
616 return SchedulerHelper::TaskQueueIdToString(
617 static_cast<SchedulerHelper::QueueId
>(queue_id
));
622 const char* RendererSchedulerImpl::PolicyToString(Policy policy
) {
626 case Policy::COMPOSITOR_PRIORITY
:
628 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY
:
629 return "compositor_critical_path";
630 case Policy::TOUCHSTART_PRIORITY
:
632 case Policy::LOADING_PRIORITY
:
640 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
641 RendererSchedulerImpl::AsValue(base::TimeTicks optional_now
) const {
642 base::AutoLock
lock(any_thread_lock_
);
643 return AsValueLocked(optional_now
);
646 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
647 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now
) const {
648 helper_
.CheckOnValidThread();
649 any_thread_lock_
.AssertAcquired();
651 if (optional_now
.is_null())
652 optional_now
= helper_
.Now();
653 scoped_refptr
<base::trace_event::TracedValue
> state
=
654 new base::trace_event::TracedValue();
656 state
->SetString("current_policy",
657 PolicyToString(MainThreadOnly().current_policy_
));
658 state
->SetString("idle_period_state",
659 IdleHelper::IdlePeriodStateToString(
660 idle_helper_
.SchedulerIdlePeriodState()));
661 state
->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden_
);
662 state
->SetDouble("now", (optional_now
- base::TimeTicks()).InMillisecondsF());
663 state
->SetDouble("last_input_signal_time",
664 (AnyThread().last_input_signal_time_
- base::TimeTicks())
666 state
->SetDouble("rails_loading_priority_deadline",
667 (AnyThread().rails_loading_priority_deadline_
-
668 base::TimeTicks()).InMillisecondsF());
669 state
->SetDouble("last_idle_period_end_time",
670 (AnyThread().last_idle_period_end_time_
- base::TimeTicks())
672 state
->SetInteger("pending_main_thread_input_event_count",
673 AnyThread().pending_main_thread_input_event_count_
);
674 state
->SetBoolean("awaiting_touch_start_response",
675 AnyThread().awaiting_touch_start_response_
);
676 state
->SetBoolean("begin_main_frame_on_critical_path",
677 AnyThread().begin_main_frame_on_critical_path_
);
678 state
->SetDouble("estimated_next_frame_begin",
679 (MainThreadOnly().estimated_next_frame_begin_
-
680 base::TimeTicks()).InMillisecondsF());
681 state
->SetBoolean("in_idle_period", AnyThread().in_idle_period_
);
686 void RendererSchedulerImpl::OnIdlePeriodStarted() {
687 base::AutoLock
lock(any_thread_lock_
);
688 AnyThread().in_idle_period_
= true;
689 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
692 void RendererSchedulerImpl::OnIdlePeriodEnded() {
693 base::AutoLock
lock(any_thread_lock_
);
694 AnyThread().last_idle_period_end_time_
= helper_
.Now();
695 AnyThread().in_idle_period_
= false;
696 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
699 void RendererSchedulerImpl::OnPageLoadStarted() {
700 base::AutoLock
lock(any_thread_lock_
);
701 AnyThread().rails_loading_priority_deadline_
=
702 helper_
.Now() + base::TimeDelta::FromMilliseconds(
703 kRailsInitialLoadingPrioritizationMillis
);
704 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED
);
707 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now
) const {
708 return (now
- AnyThread().last_idle_period_end_time_
) <=
709 base::TimeDelta::FromMilliseconds(
710 kIdlePeriodStarvationThresholdMillis
);
713 } // namespace scheduler