Revert "Reland c91b178b07b0d - Delete dead signin code (SigninGlobalError)"
[chromium-blink-merge.git] / components / scheduler / renderer / renderer_scheduler_impl.cc
blobe478bbeb2915d68a5530b2339aa2b59133c079b5
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 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",
62 this);
65 RendererSchedulerImpl::~RendererSchedulerImpl() {
66 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
67 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
68 this);
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() {
103 helper_.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())
152 return;
154 EndIdlePeriod();
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())
168 return;
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())
190 return;
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_)
200 return;
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_)
223 return;
225 end_renderer_hidden_idle_period_closure_.Cancel();
226 MainThreadOnly().renderer_hidden_ = false;
227 EndIdlePeriod();
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();
241 // static
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)) {
248 return true;
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)) {
257 return false;
259 return true;
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))
268 return;
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_;
290 if (type) {
291 switch (type) {
292 case blink::WebInputEvent::TouchStart:
293 AnyThread().awaiting_touch_start_response_ = true;
294 break;
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
301 // state.
302 if (AnyThread().awaiting_touch_start_response_ &&
303 CompositorThreadOnly().last_input_type_ ==
304 blink::WebInputEvent::TouchMove) {
305 AnyThread().awaiting_touch_start_response_ = false;
307 break;
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.
315 break;
317 default:
318 AnyThread().awaiting_touch_start_response_ = false;
319 break;
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())
352 return false;
354 MaybeUpdatePolicy();
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())
366 return false;
368 MaybeUpdatePolicy();
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_) {
375 case Policy::NORMAL:
376 return false;
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:
385 return true;
387 case Policy::LOADING_PRIORITY:
388 return false;
390 default:
391 NOTREACHED();
392 return false;
396 base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
397 const {
398 return idle_helper_.CurrentIdleTaskDeadline();
401 void RendererSchedulerImpl::MaybeUpdatePolicy() {
402 helper_.CheckOnValidThread();
403 if (policy_may_need_update_.IsSet()) {
404 UpdatePolicy();
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
411 // thread.
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())
433 return;
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_ >
440 base::TimeDelta() &&
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,
450 now);
451 } else {
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_)
457 return;
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;
470 break;
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;
475 break;
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;
480 break;
481 case Policy::NORMAL:
482 break;
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
488 // high priority.
489 break;
490 default:
491 NOTREACHED();
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:
522 return true;
524 default:
525 break;
527 return false;
530 RendererSchedulerImpl::Policy RendererSchedulerImpl::ComputeNewPolicy(
531 base::TimeTicks now,
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
541 // expensive.
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 -
575 now;
578 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
579 base::TimeTicks now,
580 base::TimeDelta* next_long_idle_period_delay_out) {
581 helper_.CheckOnValidThread();
583 MaybeUpdatePolicy();
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;
589 return false;
591 return true;
594 SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
595 return &helper_;
598 void RendererSchedulerImpl::SuspendTimerQueue() {
599 MainThreadOnly().timer_queue_suspend_count_++;
600 ForceUpdatePolicy();
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);
607 ForceUpdatePolicy();
610 // static
611 const char* RendererSchedulerImpl::PolicyToString(Policy policy) {
612 switch (policy) {
613 case Policy::NORMAL:
614 return "normal";
615 case Policy::COMPOSITOR_PRIORITY:
616 return "compositor";
617 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
618 return "compositor_critical_path";
619 case Policy::TOUCHSTART_PRIORITY:
620 return "touchstart";
621 case Policy::LOADING_PRIORITY:
622 return "loading";
623 default:
624 NOTREACHED();
625 return nullptr;
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())
654 .InMillisecondsF());
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())
660 .InMillisecondsF());
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",
668 MainThreadOnly()
669 .timer_task_cost_estimator_.expected_task_duration()
670 .InMillisecondsF());
671 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
672 state->SetDouble(
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_);
682 return state;
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