cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / components / scheduler / renderer / renderer_scheduler_impl.cc
blobf05fc4994421f2659ea92e318bd8790098ae1f19
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"
7 #include "base/bind.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"
16 namespace scheduler {
17 namespace {
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,
27 "renderer.scheduler",
28 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
29 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")),
30 idle_helper_(&helper_,
31 this,
32 "renderer.scheduler",
33 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
34 "RendererSchedulerIdlePeriod",
35 base::TimeDelta()),
36 control_task_runner_(helper_.ControlTaskRunner()),
37 compositor_task_runner_(
38 helper_.NewTaskQueue(TaskQueue::Spec("compositor_tq")
39 .SetShouldMonitorQuiescence(true))),
40 loading_task_runner_(
41 helper_.NewTaskQueue(TaskQueue::Spec("loading_tq")
42 .SetShouldMonitorQuiescence(true))),
43 timer_task_runner_(
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_),
51 weak_factory_(this) {
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",
66 this);
69 RendererSchedulerImpl::~RendererSchedulerImpl() {
70 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
71 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
72 this);
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() {
110 helper_.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())
159 return;
161 EndIdlePeriod();
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())
175 return;
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())
197 return;
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_)
207 return;
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_)
230 return;
232 end_renderer_hidden_idle_period_closure_.Cancel();
233 MainThreadOnly().renderer_hidden_ = false;
234 EndIdlePeriod();
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_)
246 return;
248 MainThreadOnly().renderer_backgrounded_ = true;
249 if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled_)
250 return;
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_)
266 return;
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();
280 // static
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)) {
287 return true;
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)) {
296 return false;
298 return true;
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))
307 return;
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_;
329 if (type) {
330 switch (type) {
331 case blink::WebInputEvent::TouchStart:
332 AnyThread().awaiting_touch_start_response_ = true;
333 break;
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
340 // state.
341 if (AnyThread().awaiting_touch_start_response_ &&
342 CompositorThreadOnly().last_input_type_ ==
343 blink::WebInputEvent::TouchMove) {
344 AnyThread().awaiting_touch_start_response_ = false;
346 break;
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.
355 break;
357 default:
358 AnyThread().awaiting_touch_start_response_ = false;
359 break;
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())
392 return false;
394 MaybeUpdatePolicy();
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())
406 return false;
408 MaybeUpdatePolicy();
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_) {
415 case Policy::NORMAL:
416 return false;
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:
425 return true;
427 case Policy::LOADING_PRIORITY:
428 return false;
430 default:
431 NOTREACHED();
432 return false;
436 base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
437 const {
438 return idle_helper_.CurrentIdleTaskDeadline();
441 void RendererSchedulerImpl::MaybeUpdatePolicy() {
442 helper_.CheckOnValidThread();
443 if (policy_may_need_update_.IsSet()) {
444 UpdatePolicy();
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
451 // thread.
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())
473 return;
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_ >
480 base::TimeDelta() &&
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,
490 now);
491 } else {
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_)
497 return;
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;
512 break;
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;
517 break;
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;
522 break;
523 case Policy::NORMAL:
524 break;
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
530 // high priority.
531 break;
532 default:
533 NOTREACHED();
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:
564 return true;
566 default:
567 break;
569 return false;
572 RendererSchedulerImpl::Policy RendererSchedulerImpl::ComputeNewPolicy(
573 base::TimeTicks now,
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
583 // expensive.
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 -
617 now;
620 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
621 base::TimeTicks now,
622 base::TimeDelta* next_long_idle_period_delay_out) {
623 helper_.CheckOnValidThread();
625 MaybeUpdatePolicy();
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;
631 return false;
633 return true;
636 SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
637 return &helper_;
640 void RendererSchedulerImpl::SuspendTimerQueue() {
641 MainThreadOnly().timer_queue_suspend_count_++;
642 ForceUpdatePolicy();
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);
649 ForceUpdatePolicy();
652 void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled(
653 bool enabled) {
654 // Note that this will only take effect for the next backgrounded signal.
655 MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled_ = enabled;
658 // static
659 const char* RendererSchedulerImpl::PolicyToString(Policy policy) {
660 switch (policy) {
661 case Policy::NORMAL:
662 return "normal";
663 case Policy::COMPOSITOR_PRIORITY:
664 return "compositor";
665 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
666 return "compositor_critical_path";
667 case Policy::TOUCHSTART_PRIORITY:
668 return "touchstart";
669 case Policy::LOADING_PRIORITY:
670 return "loading";
671 default:
672 NOTREACHED();
673 return nullptr;
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())
708 .InMillisecondsF());
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())
714 .InMillisecondsF());
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",
722 MainThreadOnly()
723 .timer_task_cost_estimator_.expected_task_duration()
724 .InMillisecondsF());
725 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
726 state->SetDouble(
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_);
736 return state;
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_)
769 return;
771 MainThreadOnly().timer_queue_suspended_when_backgrounded_ = true;
772 ForceUpdatePolicy();
775 void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() {
776 DCHECK(!MainThreadOnly().renderer_backgrounded_);
777 if (!MainThreadOnly().timer_queue_suspended_when_backgrounded_)
778 return;
780 MainThreadOnly().timer_queue_suspended_when_backgrounded_ = false;
781 ForceUpdatePolicy();
784 } // namespace scheduler