[Media Router] Add integration tests and e2e tests for media router and presentation...
[chromium-blink-merge.git] / components / scheduler / renderer / renderer_scheduler_impl.cc
blobe8b53452cdcdd50a90adfea23984f2b6e07fb51b
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/nestable_single_thread_task_runner.h"
13 #include "components/scheduler/child/prioritizing_task_queue_selector.h"
15 namespace scheduler {
17 RendererSchedulerImpl::RendererSchedulerImpl(
18 scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner)
19 : helper_(main_task_runner,
20 "renderer.scheduler",
21 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
22 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"),
23 TASK_QUEUE_COUNT),
24 idle_helper_(&helper_,
25 this,
26 IDLE_TASK_QUEUE,
27 "renderer.scheduler",
28 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
29 "RendererSchedulerIdlePeriod",
30 base::TimeDelta()),
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_),
41 weak_factory_(this) {
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;
48 i++) {
49 helper_.SetQueueName(i, TaskQueueIdToString(static_cast<QueueId>(i)));
51 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
52 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
53 this);
56 RendererSchedulerImpl::~RendererSchedulerImpl() {
57 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
58 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
59 this);
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() {
88 helper_.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())
139 return;
141 EndIdlePeriod();
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())
155 return;
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())
172 return;
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_)
182 return;
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_)
205 return;
207 end_renderer_hidden_idle_period_closure_.Cancel();
208 MainThreadOnly().renderer_hidden_ = false;
209 EndIdlePeriod();
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();
223 // static
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)) {
230 return true;
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)) {
239 return false;
241 return true;
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))
250 return;
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_;
272 if (type) {
273 switch (type) {
274 case blink::WebInputEvent::TouchStart:
275 AnyThread().awaiting_touch_start_response_ = true;
276 break;
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
283 // state.
284 if (AnyThread().awaiting_touch_start_response_ &&
285 CompositorThreadOnly().last_input_type_ ==
286 blink::WebInputEvent::TouchMove) {
287 AnyThread().awaiting_touch_start_response_ = false;
289 break;
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.
297 break;
299 default:
300 AnyThread().awaiting_touch_start_response_ = false;
301 break;
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())
334 return false;
336 MaybeUpdatePolicy();
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())
348 return false;
350 MaybeUpdatePolicy();
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_) {
357 case Policy::NORMAL:
358 return false;
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:
367 return true;
369 case Policy::LOADING_PRIORITY:
370 return false;
372 default:
373 NOTREACHED();
374 return false;
378 base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
379 const {
380 return idle_helper_.CurrentIdleTaskDeadline();
383 void RendererSchedulerImpl::MaybeUpdatePolicy() {
384 helper_.CheckOnValidThread();
385 if (policy_may_need_update_.IsSet()) {
386 UpdatePolicy();
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
393 // thread.
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())
415 return;
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,
426 now);
427 } else {
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_)
433 return;
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);
445 break;
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(
452 LOADING_TASK_QUEUE,
453 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY);
454 helper_.DisableQueue(LOADING_TASK_QUEUE);
455 policy_disables_timer_queue = true;
456 break;
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;
462 break;
463 case Policy::NORMAL:
464 helper_.SetQueuePriority(COMPOSITOR_TASK_QUEUE,
465 PrioritizingTaskQueueSelector::NORMAL_PRIORITY);
466 helper_.SetQueuePriority(LOADING_TASK_QUEUE,
467 PrioritizingTaskQueueSelector::NORMAL_PRIORITY);
468 break;
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
479 // high priority.
480 break;
481 default:
482 NOTREACHED();
484 if (MainThreadOnly().timer_queue_suspend_count_ != 0 ||
485 policy_disables_timer_queue) {
486 helper_.DisableQueue(TIMER_TASK_QUEUE);
487 } else {
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:
511 return true;
513 default:
514 break;
516 return false;
519 RendererSchedulerImpl::Policy RendererSchedulerImpl::ComputeNewPolicy(
520 base::TimeTicks now,
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 -
569 now;
572 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
573 base::TimeTicks now,
574 base::TimeDelta* next_long_idle_period_delay_out) {
575 helper_.CheckOnValidThread();
577 MaybeUpdatePolicy();
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;
583 return false;
585 return true;
588 SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
589 return &helper_;
592 void RendererSchedulerImpl::SuspendTimerQueue() {
593 MainThreadOnly().timer_queue_suspend_count_++;
594 ForceUpdatePolicy();
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);
601 ForceUpdatePolicy();
604 // static
605 const char* RendererSchedulerImpl::TaskQueueIdToString(QueueId queue_id) {
606 switch (queue_id) {
607 case IDLE_TASK_QUEUE:
608 return "idle_tq";
609 case COMPOSITOR_TASK_QUEUE:
610 return "compositor_tq";
611 case LOADING_TASK_QUEUE:
612 return "loading_tq";
613 case TIMER_TASK_QUEUE:
614 return "timer_tq";
615 default:
616 return SchedulerHelper::TaskQueueIdToString(
617 static_cast<SchedulerHelper::QueueId>(queue_id));
621 // static
622 const char* RendererSchedulerImpl::PolicyToString(Policy policy) {
623 switch (policy) {
624 case Policy::NORMAL:
625 return "normal";
626 case Policy::COMPOSITOR_PRIORITY:
627 return "compositor";
628 case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
629 return "compositor_critical_path";
630 case Policy::TOUCHSTART_PRIORITY:
631 return "touchstart";
632 case Policy::LOADING_PRIORITY:
633 return "loading";
634 default:
635 NOTREACHED();
636 return nullptr;
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())
665 .InMillisecondsF());
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())
671 .InMillisecondsF());
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_);
683 return state;
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