Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: components/scheduler/renderer/renderer_scheduler_impl.cc

Issue 2118903002: scheduler: Move the Blink scheduler into Blink (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Another GYP fix Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "components/scheduler/renderer/renderer_scheduler_impl.h"
6
7 #include "base/bind.h"
8 #include "base/debug/stack_trace.h"
9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/trace_event/trace_event.h"
13 #include "base/trace_event/trace_event_argument.h"
14 #include "cc/output/begin_frame_args.h"
15 #include "components/scheduler/base/task_queue_impl.h"
16 #include "components/scheduler/base/task_queue_selector.h"
17 #include "components/scheduler/base/virtual_time_domain.h"
18 #include "components/scheduler/child/scheduler_tqm_delegate.h"
19 #include "components/scheduler/renderer/web_view_scheduler_impl.h"
20 #include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
21
22 namespace scheduler {
23 namespace {
24 // The run time of loading tasks is strongly bimodal. The vast majority are
25 // very cheap, but there are usually a handful of very expensive tasks (e.g ~1
26 // second on a mobile device) so we take a very pessimistic view when estimating
27 // the cost of loading tasks.
28 const int kLoadingTaskEstimationSampleCount = 1000;
29 const double kLoadingTaskEstimationPercentile = 99;
30 const int kTimerTaskEstimationSampleCount = 1000;
31 const double kTimerTaskEstimationPercentile = 99;
32 const int kShortIdlePeriodDurationSampleCount = 10;
33 const double kShortIdlePeriodDurationPercentile = 50;
34 // Amount of idle time left in a frame (as a ratio of the vsync interval) above
35 // which main thread compositing can be considered fast.
36 const double kFastCompositingIdleTimeThreshold = .2;
37 } // namespace
38
39 RendererSchedulerImpl::RendererSchedulerImpl(
40 scoped_refptr<SchedulerTqmDelegate> main_task_runner)
41 : helper_(main_task_runner,
42 "renderer.scheduler",
43 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
44 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")),
45 idle_helper_(&helper_,
46 this,
47 "renderer.scheduler",
48 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
49 "RendererSchedulerIdlePeriod",
50 base::TimeDelta()),
51 render_widget_scheduler_signals_(this),
52 control_task_runner_(helper_.ControlTaskRunner()),
53 compositor_task_runner_(helper_.NewTaskQueue(
54 TaskQueue::Spec("compositor_tq").SetShouldMonitorQuiescence(true))),
55 delayed_update_policy_runner_(
56 base::Bind(&RendererSchedulerImpl::UpdatePolicy,
57 base::Unretained(this)),
58 helper_.ControlTaskRunner()),
59 main_thread_only_(this,
60 compositor_task_runner_,
61 helper_.scheduler_tqm_delegate().get()),
62 policy_may_need_update_(&any_thread_lock_),
63 weak_factory_(this) {
64 throttling_helper_.reset(new ThrottlingHelper(this, "renderer.scheduler"));
65 update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy,
66 weak_factory_.GetWeakPtr());
67 end_renderer_hidden_idle_period_closure_.Reset(base::Bind(
68 &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr()));
69
70 suspend_timers_when_backgrounded_closure_.Reset(
71 base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded,
72 weak_factory_.GetWeakPtr()));
73
74 default_loading_task_runner_ = NewLoadingTaskRunner("default_loading_tq");
75 default_timer_task_runner_ = NewTimerTaskRunner("default_timer_tq");
76
77 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
78 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
79 this);
80
81 helper_.SetObserver(this);
82 helper_.SetTaskTimeTracker(this);
83 }
84
85 RendererSchedulerImpl::~RendererSchedulerImpl() {
86 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
87 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
88 this);
89
90 for (const scoped_refptr<TaskQueue>& loading_queue : loading_task_runners_) {
91 loading_queue->RemoveTaskObserver(
92 &MainThreadOnly().loading_task_cost_estimator);
93 }
94 for (const scoped_refptr<TaskQueue>& timer_queue : timer_task_runners_) {
95 timer_queue->RemoveTaskObserver(
96 &MainThreadOnly().timer_task_cost_estimator);
97 }
98
99 // Ensure the renderer scheduler was shut down explicitly, because otherwise
100 // we could end up having stale pointers to the Blink heap which has been
101 // terminated by this point.
102 DCHECK(MainThreadOnly().was_shutdown);
103 }
104
105 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly(
106 RendererSchedulerImpl* renderer_scheduler_impl,
107 const scoped_refptr<TaskQueue>& compositor_task_runner,
108 base::TickClock* time_source)
109 : loading_task_cost_estimator(time_source,
110 kLoadingTaskEstimationSampleCount,
111 kLoadingTaskEstimationPercentile),
112 timer_task_cost_estimator(time_source,
113 kTimerTaskEstimationSampleCount,
114 kTimerTaskEstimationPercentile),
115 queueing_time_estimator(renderer_scheduler_impl,
116 base::TimeDelta::FromSeconds(1)),
117 idle_time_estimator(compositor_task_runner,
118 time_source,
119 kShortIdlePeriodDurationSampleCount,
120 kShortIdlePeriodDurationPercentile),
121 current_use_case(UseCase::NONE),
122 timer_queue_suspend_count(0),
123 navigation_task_expected_count(0),
124 expensive_task_policy(ExpensiveTaskPolicy::RUN),
125 renderer_hidden(false),
126 renderer_backgrounded(false),
127 renderer_suspended(false),
128 timer_queue_suspension_when_backgrounded_enabled(false),
129 timer_queue_suspended_when_backgrounded(false),
130 was_shutdown(false),
131 loading_tasks_seem_expensive(false),
132 timer_tasks_seem_expensive(false),
133 touchstart_expected_soon(false),
134 have_seen_a_begin_main_frame(false),
135 have_reported_blocking_intervention_in_current_policy(false),
136 have_reported_blocking_intervention_since_navigation(false),
137 has_visible_render_widget_with_touch_handler(false),
138 begin_frame_not_expected_soon(false),
139 expensive_task_blocking_allowed(true),
140 in_idle_period_for_testing(false),
141 rail_mode_observer(nullptr) {}
142
143 RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
144
145 RendererSchedulerImpl::AnyThread::AnyThread()
146 : awaiting_touch_start_response(false),
147 in_idle_period(false),
148 begin_main_frame_on_critical_path(false),
149 last_gesture_was_compositor_driven(false),
150 default_gesture_prevented(true),
151 have_seen_touchstart(false) {}
152
153 RendererSchedulerImpl::AnyThread::~AnyThread() {}
154
155 RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
156 : last_input_type(blink::WebInputEvent::Undefined) {}
157
158 RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {}
159
160 void RendererSchedulerImpl::Shutdown() {
161 throttling_helper_.reset();
162 helper_.Shutdown();
163 MainThreadOnly().was_shutdown = true;
164 MainThreadOnly().rail_mode_observer = nullptr;
165 }
166
167 std::unique_ptr<blink::WebThread> RendererSchedulerImpl::CreateMainThread() {
168 return base::WrapUnique(new WebThreadImplForRendererScheduler(this));
169 }
170
171 scoped_refptr<TaskQueue> RendererSchedulerImpl::DefaultTaskRunner() {
172 return helper_.DefaultTaskRunner();
173 }
174
175 scoped_refptr<TaskQueue> RendererSchedulerImpl::CompositorTaskRunner() {
176 helper_.CheckOnValidThread();
177 return compositor_task_runner_;
178 }
179
180 scoped_refptr<SingleThreadIdleTaskRunner>
181 RendererSchedulerImpl::IdleTaskRunner() {
182 return idle_helper_.IdleTaskRunner();
183 }
184
185 scoped_refptr<TaskQueue> RendererSchedulerImpl::LoadingTaskRunner() {
186 helper_.CheckOnValidThread();
187 return default_loading_task_runner_;
188 }
189
190 scoped_refptr<TaskQueue> RendererSchedulerImpl::TimerTaskRunner() {
191 helper_.CheckOnValidThread();
192 return default_timer_task_runner_;
193 }
194
195 scoped_refptr<TaskQueue> RendererSchedulerImpl::ControlTaskRunner() {
196 helper_.CheckOnValidThread();
197 return helper_.ControlTaskRunner();
198 }
199
200 scoped_refptr<TaskQueue> RendererSchedulerImpl::NewLoadingTaskRunner(
201 const char* name) {
202 helper_.CheckOnValidThread();
203 scoped_refptr<TaskQueue> loading_task_queue(helper_.NewTaskQueue(
204 TaskQueue::Spec(name).SetShouldMonitorQuiescence(true)));
205 loading_task_runners_.insert(loading_task_queue);
206 loading_task_queue->SetQueueEnabled(
207 MainThreadOnly().current_policy.loading_queue_policy.is_enabled);
208 loading_task_queue->SetQueuePriority(
209 MainThreadOnly().current_policy.loading_queue_policy.priority);
210 if (MainThreadOnly().current_policy.loading_queue_policy.time_domain_type ==
211 TimeDomainType::THROTTLED) {
212 throttling_helper_->IncreaseThrottleRefCount(loading_task_queue.get());
213 }
214 loading_task_queue->AddTaskObserver(
215 &MainThreadOnly().loading_task_cost_estimator);
216 return loading_task_queue;
217 }
218
219 scoped_refptr<TaskQueue> RendererSchedulerImpl::NewTimerTaskRunner(
220 const char* name) {
221 helper_.CheckOnValidThread();
222 scoped_refptr<TaskQueue> timer_task_queue(
223 helper_.NewTaskQueue(TaskQueue::Spec(name)
224 .SetShouldMonitorQuiescence(true)
225 .SetShouldReportWhenExecutionBlocked(true)));
226 timer_task_runners_.insert(timer_task_queue);
227 timer_task_queue->SetQueueEnabled(
228 MainThreadOnly().current_policy.timer_queue_policy.is_enabled);
229 timer_task_queue->SetQueuePriority(
230 MainThreadOnly().current_policy.timer_queue_policy.priority);
231 if (MainThreadOnly().current_policy.timer_queue_policy.time_domain_type ==
232 TimeDomainType::THROTTLED) {
233 throttling_helper_->IncreaseThrottleRefCount(timer_task_queue.get());
234 }
235 timer_task_queue->AddTaskObserver(
236 &MainThreadOnly().timer_task_cost_estimator);
237 return timer_task_queue;
238 }
239
240 scoped_refptr<TaskQueue> RendererSchedulerImpl::NewUnthrottledTaskRunner(
241 const char* name) {
242 helper_.CheckOnValidThread();
243 scoped_refptr<TaskQueue> unthrottled_task_queue(helper_.NewTaskQueue(
244 TaskQueue::Spec(name).SetShouldMonitorQuiescence(true)));
245 return unthrottled_task_queue;
246 }
247
248 std::unique_ptr<RenderWidgetSchedulingState>
249 RendererSchedulerImpl::NewRenderWidgetSchedulingState() {
250 return render_widget_scheduler_signals_.NewRenderWidgetSchedulingState();
251 }
252
253 void RendererSchedulerImpl::OnUnregisterTaskQueue(
254 const scoped_refptr<TaskQueue>& task_queue) {
255 if (throttling_helper_.get())
256 throttling_helper_->UnregisterTaskQueue(task_queue.get());
257
258 if (loading_task_runners_.find(task_queue) != loading_task_runners_.end()) {
259 task_queue->RemoveTaskObserver(
260 &MainThreadOnly().loading_task_cost_estimator);
261 loading_task_runners_.erase(task_queue);
262 } else if (timer_task_runners_.find(task_queue) !=
263 timer_task_runners_.end()) {
264 task_queue->RemoveTaskObserver(&MainThreadOnly().timer_task_cost_estimator);
265 timer_task_runners_.erase(task_queue);
266 }
267 }
268
269 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
270 return idle_helper_.CanExceedIdleDeadlineIfRequired();
271 }
272
273 void RendererSchedulerImpl::AddTaskObserver(
274 base::MessageLoop::TaskObserver* task_observer) {
275 helper_.AddTaskObserver(task_observer);
276 }
277
278 void RendererSchedulerImpl::RemoveTaskObserver(
279 base::MessageLoop::TaskObserver* task_observer) {
280 helper_.RemoveTaskObserver(task_observer);
281 }
282
283 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs& args) {
284 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
285 "RendererSchedulerImpl::WillBeginFrame", "args", args.AsValue());
286 helper_.CheckOnValidThread();
287 if (helper_.IsShutdown())
288 return;
289
290 EndIdlePeriod();
291 MainThreadOnly().estimated_next_frame_begin = args.frame_time + args.interval;
292 MainThreadOnly().have_seen_a_begin_main_frame = true;
293 MainThreadOnly().begin_frame_not_expected_soon = false;
294 MainThreadOnly().compositor_frame_interval = args.interval;
295 {
296 base::AutoLock lock(any_thread_lock_);
297 AnyThread().begin_main_frame_on_critical_path = args.on_critical_path;
298 }
299 }
300
301 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
302 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
303 "RendererSchedulerImpl::DidCommitFrameToCompositor");
304 helper_.CheckOnValidThread();
305 if (helper_.IsShutdown())
306 return;
307
308 base::TimeTicks now(helper_.scheduler_tqm_delegate()->NowTicks());
309 if (now < MainThreadOnly().estimated_next_frame_begin) {
310 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
311 // the next pending delayed tasks (as currently done in for long idle times)
312 idle_helper_.StartIdlePeriod(
313 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now,
314 MainThreadOnly().estimated_next_frame_begin);
315 }
316
317 MainThreadOnly().idle_time_estimator.DidCommitFrameToCompositor();
318 }
319
320 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
321 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
322 "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
323 helper_.CheckOnValidThread();
324 if (helper_.IsShutdown())
325 return;
326
327 MainThreadOnly().begin_frame_not_expected_soon = true;
328 idle_helper_.EnableLongIdlePeriod();
329 {
330 base::AutoLock lock(any_thread_lock_);
331 AnyThread().begin_main_frame_on_critical_path = false;
332 }
333 }
334
335 void RendererSchedulerImpl::SetAllRenderWidgetsHidden(bool hidden) {
336 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
337 "RendererSchedulerImpl::SetAllRenderWidgetsHidden", "hidden",
338 hidden);
339
340 helper_.CheckOnValidThread();
341
342 if (helper_.IsShutdown() || MainThreadOnly().renderer_hidden == hidden)
343 return;
344
345 end_renderer_hidden_idle_period_closure_.Cancel();
346
347 if (hidden) {
348 idle_helper_.EnableLongIdlePeriod();
349
350 // Ensure that we stop running idle tasks after a few seconds of being
351 // hidden.
352 base::TimeDelta end_idle_when_hidden_delay =
353 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis);
354 control_task_runner_->PostDelayedTask(
355 FROM_HERE, end_renderer_hidden_idle_period_closure_.callback(),
356 end_idle_when_hidden_delay);
357 MainThreadOnly().renderer_hidden = true;
358 } else {
359 MainThreadOnly().renderer_hidden = false;
360 EndIdlePeriod();
361 }
362
363 // TODO(alexclarke): Should we update policy here?
364 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
365 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
366 this, AsValue(helper_.scheduler_tqm_delegate()->NowTicks()));
367 }
368
369 void RendererSchedulerImpl::SetHasVisibleRenderWidgetWithTouchHandler(
370 bool has_visible_render_widget_with_touch_handler) {
371 helper_.CheckOnValidThread();
372 if (has_visible_render_widget_with_touch_handler ==
373 MainThreadOnly().has_visible_render_widget_with_touch_handler)
374 return;
375
376 MainThreadOnly().has_visible_render_widget_with_touch_handler =
377 has_visible_render_widget_with_touch_handler;
378
379 base::AutoLock lock(any_thread_lock_);
380 UpdatePolicyLocked(UpdateType::FORCE_UPDATE);
381 }
382
383 void RendererSchedulerImpl::OnRendererBackgrounded() {
384 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
385 "RendererSchedulerImpl::OnRendererBackgrounded");
386 helper_.CheckOnValidThread();
387 if (helper_.IsShutdown() || MainThreadOnly().renderer_backgrounded)
388 return;
389
390 MainThreadOnly().renderer_backgrounded = true;
391 if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled)
392 return;
393
394 suspend_timers_when_backgrounded_closure_.Cancel();
395 base::TimeDelta suspend_timers_when_backgrounded_delay =
396 base::TimeDelta::FromMilliseconds(
397 kSuspendTimersWhenBackgroundedDelayMillis);
398 control_task_runner_->PostDelayedTask(
399 FROM_HERE, suspend_timers_when_backgrounded_closure_.callback(),
400 suspend_timers_when_backgrounded_delay);
401 }
402
403 void RendererSchedulerImpl::OnRendererForegrounded() {
404 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
405 "RendererSchedulerImpl::OnRendererForegrounded");
406 helper_.CheckOnValidThread();
407 if (helper_.IsShutdown() || !MainThreadOnly().renderer_backgrounded)
408 return;
409
410 MainThreadOnly().renderer_backgrounded = false;
411 MainThreadOnly().renderer_suspended = false;
412 suspend_timers_when_backgrounded_closure_.Cancel();
413 ResumeTimerQueueWhenForegrounded();
414 }
415
416 void RendererSchedulerImpl::SuspendRenderer() {
417 helper_.CheckOnValidThread();
418 DCHECK(MainThreadOnly().renderer_backgrounded);
419 if (helper_.IsShutdown())
420 return;
421 suspend_timers_when_backgrounded_closure_.Cancel();
422 // TODO(hajimehoshi): We might need to suspend not only timer queue but also
423 // e.g. loading tasks or postMessage.
424 MainThreadOnly().renderer_suspended = true;
425 SuspendTimerQueueWhenBackgrounded();
426 }
427
428 void RendererSchedulerImpl::EndIdlePeriod() {
429 if (MainThreadOnly().in_idle_period_for_testing)
430 return;
431 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
432 "RendererSchedulerImpl::EndIdlePeriod");
433 helper_.CheckOnValidThread();
434 idle_helper_.EndIdlePeriod();
435 }
436
437 void RendererSchedulerImpl::EndIdlePeriodForTesting(
438 const base::Closure& callback,
439 base::TimeTicks time_remaining) {
440 MainThreadOnly().in_idle_period_for_testing = false;
441 EndIdlePeriod();
442 callback.Run();
443 }
444
445 bool RendererSchedulerImpl::PolicyNeedsUpdateForTesting() {
446 return policy_may_need_update_.IsSet();
447 }
448
449 // static
450 bool RendererSchedulerImpl::ShouldPrioritizeInputEvent(
451 const blink::WebInputEvent& web_input_event) {
452 // We regard MouseMove events with the left mouse button down as a signal
453 // that the user is doing something requiring a smooth frame rate.
454 if (web_input_event.type == blink::WebInputEvent::MouseMove &&
455 (web_input_event.modifiers & blink::WebInputEvent::LeftButtonDown)) {
456 return true;
457 }
458 // Ignore all other mouse events because they probably don't signal user
459 // interaction needing a smooth framerate. NOTE isMouseEventType returns false
460 // for mouse wheel events, hence we regard them as user input.
461 // Ignore keyboard events because it doesn't really make sense to enter
462 // compositor priority for them.
463 if (blink::WebInputEvent::isMouseEventType(web_input_event.type) ||
464 blink::WebInputEvent::isKeyboardEventType(web_input_event.type)) {
465 return false;
466 }
467 return true;
468 }
469
470 void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread(
471 const blink::WebInputEvent& web_input_event,
472 InputEventState event_state) {
473 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
474 "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread");
475 if (!ShouldPrioritizeInputEvent(web_input_event))
476 return;
477
478 UpdateForInputEventOnCompositorThread(web_input_event.type, event_state);
479 }
480
481 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
482 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
483 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread");
484 base::AutoLock lock(any_thread_lock_);
485 AnyThread().fling_compositor_escalation_deadline =
486 helper_.scheduler_tqm_delegate()->NowTicks() +
487 base::TimeDelta::FromMilliseconds(kFlingEscalationLimitMillis);
488 }
489
490 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread(
491 blink::WebInputEvent::Type type,
492 InputEventState input_event_state) {
493 base::AutoLock lock(any_thread_lock_);
494 base::TimeTicks now = helper_.scheduler_tqm_delegate()->NowTicks();
495
496 // TODO(alexclarke): Move WebInputEventTraits where we can access it from here
497 // and record the name rather than the integer representation.
498 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
499 "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread",
500 "type", static_cast<int>(type), "input_event_state",
501 InputEventStateToString(input_event_state));
502
503 base::TimeDelta unused_policy_duration;
504 UseCase previous_use_case =
505 ComputeCurrentUseCase(now, &unused_policy_duration);
506 bool was_awaiting_touch_start_response =
507 AnyThread().awaiting_touch_start_response;
508
509 AnyThread().user_model.DidStartProcessingInputEvent(type, now);
510
511 if (input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR)
512 AnyThread().user_model.DidFinishProcessingInputEvent(now);
513
514 if (type) {
515 switch (type) {
516 case blink::WebInputEvent::TouchStart:
517 AnyThread().awaiting_touch_start_response = true;
518 // This is just a fail-safe to reset the state of
519 // |last_gesture_was_compositor_driven| to the default. We don't know
520 // yet where the gesture will run.
521 AnyThread().last_gesture_was_compositor_driven = false;
522 AnyThread().have_seen_touchstart = true;
523 // Assume the default gesture is prevented until we see evidence
524 // otherwise.
525 AnyThread().default_gesture_prevented = true;
526 break;
527
528 case blink::WebInputEvent::TouchMove:
529 // Observation of consecutive touchmoves is a strong signal that the
530 // page is consuming the touch sequence, in which case touchstart
531 // response prioritization is no longer necessary. Otherwise, the
532 // initial touchmove should preserve the touchstart response pending
533 // state.
534 if (AnyThread().awaiting_touch_start_response &&
535 CompositorThreadOnly().last_input_type ==
536 blink::WebInputEvent::TouchMove) {
537 AnyThread().awaiting_touch_start_response = false;
538 }
539 break;
540
541 case blink::WebInputEvent::GesturePinchUpdate:
542 case blink::WebInputEvent::GestureScrollUpdate:
543 // If we see events for an established gesture, we can lock it to the
544 // appropriate thread as the gesture can no longer be cancelled.
545 AnyThread().last_gesture_was_compositor_driven =
546 input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR;
547 AnyThread().awaiting_touch_start_response = false;
548 AnyThread().default_gesture_prevented = false;
549 break;
550
551 case blink::WebInputEvent::GestureFlingCancel:
552 AnyThread().fling_compositor_escalation_deadline = base::TimeTicks();
553 break;
554
555 case blink::WebInputEvent::GestureTapDown:
556 case blink::WebInputEvent::GestureShowPress:
557 case blink::WebInputEvent::GestureScrollEnd:
558 // With no observable effect, these meta events do not indicate a
559 // meaningful touchstart response and should not impact task priority.
560 break;
561
562 default:
563 AnyThread().awaiting_touch_start_response = false;
564 break;
565 }
566 }
567
568 // Avoid unnecessary policy updates if the use case did not change.
569 UseCase use_case = ComputeCurrentUseCase(now, &unused_policy_duration);
570
571 if (use_case != previous_use_case ||
572 was_awaiting_touch_start_response !=
573 AnyThread().awaiting_touch_start_response) {
574 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE);
575 }
576 CompositorThreadOnly().last_input_type = type;
577 }
578
579 void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
580 const blink::WebInputEvent& web_input_event) {
581 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
582 "RendererSchedulerImpl::DidHandleInputEventOnMainThread");
583 helper_.CheckOnValidThread();
584 if (ShouldPrioritizeInputEvent(web_input_event)) {
585 base::AutoLock lock(any_thread_lock_);
586 AnyThread().user_model.DidFinishProcessingInputEvent(
587 helper_.scheduler_tqm_delegate()->NowTicks());
588 }
589 }
590
591 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() {
592 helper_.CheckOnValidThread();
593 if (helper_.IsShutdown())
594 return false;
595
596 MaybeUpdatePolicy();
597 // The touchstart, synchronized gesture and main-thread gesture use cases
598 // indicate a strong likelihood of high-priority work in the near future.
599 UseCase use_case = MainThreadOnly().current_use_case;
600 return MainThreadOnly().touchstart_expected_soon ||
601 use_case == UseCase::TOUCHSTART ||
602 use_case == UseCase::MAIN_THREAD_GESTURE ||
603 use_case == UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING ||
604 use_case == UseCase::SYNCHRONIZED_GESTURE;
605 }
606
607 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
608 helper_.CheckOnValidThread();
609 if (helper_.IsShutdown())
610 return false;
611
612 MaybeUpdatePolicy();
613 // We only yield if there's a urgent task to be run now, or we are expecting
614 // one soon (touch start).
615 // Note: even though the control queue has the highest priority we don't yield
616 // for it since these tasks are not user-provided work and they are only
617 // intended to run before the next task, not interrupt the tasks.
618 switch (MainThreadOnly().current_use_case) {
619 case UseCase::COMPOSITOR_GESTURE:
620 case UseCase::NONE:
621 return MainThreadOnly().touchstart_expected_soon;
622
623 case UseCase::MAIN_THREAD_GESTURE:
624 case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING:
625 case UseCase::SYNCHRONIZED_GESTURE:
626 return compositor_task_runner_->HasPendingImmediateWork() ||
627 MainThreadOnly().touchstart_expected_soon;
628
629 case UseCase::TOUCHSTART:
630 return true;
631
632 case UseCase::LOADING:
633 return false;
634
635 default:
636 NOTREACHED();
637 return false;
638 }
639 }
640
641 base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
642 const {
643 return idle_helper_.CurrentIdleTaskDeadline();
644 }
645
646 void RendererSchedulerImpl::RunIdleTasksForTesting(
647 const base::Closure& callback) {
648 MainThreadOnly().in_idle_period_for_testing = true;
649 IdleTaskRunner()->PostIdleTask(
650 FROM_HERE,
651 base::Bind(&RendererSchedulerImpl::EndIdlePeriodForTesting,
652 weak_factory_.GetWeakPtr(), callback));
653 idle_helper_.EnableLongIdlePeriod();
654 }
655
656 void RendererSchedulerImpl::MaybeUpdatePolicy() {
657 helper_.CheckOnValidThread();
658 if (policy_may_need_update_.IsSet()) {
659 UpdatePolicy();
660 }
661 }
662
663 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
664 const tracked_objects::Location& from_here) {
665 // TODO(scheduler-dev): Check that this method isn't called from the main
666 // thread.
667 any_thread_lock_.AssertAcquired();
668 if (!policy_may_need_update_.IsSet()) {
669 policy_may_need_update_.SetWhileLocked(true);
670 control_task_runner_->PostTask(from_here, update_policy_closure_);
671 }
672 }
673
674 void RendererSchedulerImpl::UpdatePolicy() {
675 base::AutoLock lock(any_thread_lock_);
676 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
677 }
678
679 void RendererSchedulerImpl::ForceUpdatePolicy() {
680 base::AutoLock lock(any_thread_lock_);
681 UpdatePolicyLocked(UpdateType::FORCE_UPDATE);
682 }
683
684 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) {
685 helper_.CheckOnValidThread();
686 any_thread_lock_.AssertAcquired();
687 if (helper_.IsShutdown())
688 return;
689
690 base::TimeTicks now = helper_.scheduler_tqm_delegate()->NowTicks();
691 policy_may_need_update_.SetWhileLocked(false);
692
693 base::TimeDelta expected_use_case_duration;
694 UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration);
695 MainThreadOnly().current_use_case = use_case;
696
697 base::TimeDelta touchstart_expected_flag_valid_for_duration;
698 bool touchstart_expected_soon = false;
699 if (MainThreadOnly().has_visible_render_widget_with_touch_handler) {
700 touchstart_expected_soon = AnyThread().user_model.IsGestureExpectedSoon(
701 now, &touchstart_expected_flag_valid_for_duration);
702 }
703 MainThreadOnly().touchstart_expected_soon = touchstart_expected_soon;
704
705 base::TimeDelta longest_jank_free_task_duration =
706 EstimateLongestJankFreeTaskDuration();
707 MainThreadOnly().longest_jank_free_task_duration =
708 longest_jank_free_task_duration;
709
710 bool loading_tasks_seem_expensive = false;
711 bool timer_tasks_seem_expensive = false;
712 loading_tasks_seem_expensive =
713 MainThreadOnly().loading_task_cost_estimator.expected_task_duration() >
714 longest_jank_free_task_duration;
715 timer_tasks_seem_expensive =
716 MainThreadOnly().timer_task_cost_estimator.expected_task_duration() >
717 longest_jank_free_task_duration;
718 MainThreadOnly().timer_tasks_seem_expensive = timer_tasks_seem_expensive;
719 MainThreadOnly().loading_tasks_seem_expensive = loading_tasks_seem_expensive;
720
721 // The |new_policy_duration| is the minimum of |expected_use_case_duration|
722 // and |touchstart_expected_flag_valid_for_duration| unless one is zero in
723 // which case we choose the other.
724 base::TimeDelta new_policy_duration = expected_use_case_duration;
725 if (new_policy_duration.is_zero() ||
726 (touchstart_expected_flag_valid_for_duration > base::TimeDelta() &&
727 new_policy_duration > touchstart_expected_flag_valid_for_duration)) {
728 new_policy_duration = touchstart_expected_flag_valid_for_duration;
729 }
730
731 if (new_policy_duration > base::TimeDelta()) {
732 MainThreadOnly().current_policy_expiration_time = now + new_policy_duration;
733 delayed_update_policy_runner_.SetDeadline(FROM_HERE, new_policy_duration,
734 now);
735 } else {
736 MainThreadOnly().current_policy_expiration_time = base::TimeTicks();
737 }
738
739 // Avoid prioritizing main thread compositing (e.g., rAF) if it is extremely
740 // slow, because that can cause starvation in other task sources.
741 bool main_thread_compositing_is_fast =
742 MainThreadOnly().idle_time_estimator.GetExpectedIdleDuration(
743 MainThreadOnly().compositor_frame_interval) >
744 MainThreadOnly().compositor_frame_interval *
745 kFastCompositingIdleTimeThreshold;
746
747 Policy new_policy;
748 ExpensiveTaskPolicy expensive_task_policy = ExpensiveTaskPolicy::RUN;
749 new_policy.rail_mode = v8::PERFORMANCE_ANIMATION;
750
751 switch (use_case) {
752 case UseCase::COMPOSITOR_GESTURE:
753 if (touchstart_expected_soon) {
754 new_policy.rail_mode = v8::PERFORMANCE_RESPONSE;
755 expensive_task_policy = ExpensiveTaskPolicy::BLOCK;
756 new_policy.compositor_queue_policy.priority = TaskQueue::HIGH_PRIORITY;
757 } else {
758 // What we really want to do is priorize loading tasks, but that doesn't
759 // seem to be safe. Instead we do that by proxy by deprioritizing
760 // compositor tasks. This should be safe since we've already gone to the
761 // pain of fixing ordering issues with them.
762 new_policy.compositor_queue_policy.priority =
763 TaskQueue::BEST_EFFORT_PRIORITY;
764 }
765 break;
766
767 case UseCase::SYNCHRONIZED_GESTURE:
768 new_policy.compositor_queue_policy.priority =
769 main_thread_compositing_is_fast ? TaskQueue::HIGH_PRIORITY
770 : TaskQueue::NORMAL_PRIORITY;
771 if (touchstart_expected_soon) {
772 new_policy.rail_mode = v8::PERFORMANCE_RESPONSE;
773 expensive_task_policy = ExpensiveTaskPolicy::BLOCK;
774 } else {
775 expensive_task_policy = ExpensiveTaskPolicy::THROTTLE;
776 }
777 break;
778
779 case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING:
780 // In main thread input handling scenarios we don't have perfect knowledge
781 // about which things we should be prioritizing, so we don't attempt to
782 // block expensive tasks because we don't know whether they were integral
783 // to the page's functionality or not.
784 new_policy.compositor_queue_policy.priority =
785 main_thread_compositing_is_fast ? TaskQueue::HIGH_PRIORITY
786 : TaskQueue::NORMAL_PRIORITY;
787 break;
788
789 case UseCase::MAIN_THREAD_GESTURE:
790 // A main thread gesture is for example a scroll gesture which is handled
791 // by the main thread. Since we know the established gesture type, we can
792 // be a little more aggressive about prioritizing compositing and input
793 // handling over other tasks.
794 new_policy.compositor_queue_policy.priority = TaskQueue::HIGH_PRIORITY;
795 if (touchstart_expected_soon) {
796 new_policy.rail_mode = v8::PERFORMANCE_RESPONSE;
797 expensive_task_policy = ExpensiveTaskPolicy::BLOCK;
798 } else {
799 expensive_task_policy = ExpensiveTaskPolicy::THROTTLE;
800 }
801 break;
802
803 case UseCase::TOUCHSTART:
804 new_policy.rail_mode = v8::PERFORMANCE_RESPONSE;
805 new_policy.compositor_queue_policy.priority = TaskQueue::HIGH_PRIORITY;
806 new_policy.loading_queue_policy.is_enabled = false;
807 new_policy.timer_queue_policy.is_enabled = false;
808 // NOTE this is a nop due to the above.
809 expensive_task_policy = ExpensiveTaskPolicy::BLOCK;
810 break;
811
812 case UseCase::NONE:
813 // It's only safe to block tasks that if we are expecting a compositor
814 // driven gesture.
815 if (touchstart_expected_soon &&
816 AnyThread().last_gesture_was_compositor_driven) {
817 new_policy.rail_mode = v8::PERFORMANCE_RESPONSE;
818 expensive_task_policy = ExpensiveTaskPolicy::BLOCK;
819 }
820 break;
821
822 case UseCase::LOADING:
823 new_policy.rail_mode = v8::PERFORMANCE_LOAD;
824 new_policy.loading_queue_policy.priority = TaskQueue::HIGH_PRIORITY;
825 new_policy.default_queue_policy.priority = TaskQueue::HIGH_PRIORITY;
826 break;
827
828 default:
829 NOTREACHED();
830 }
831
832 // TODO(skyostil): Add an idle state for foreground tabs too.
833 if (MainThreadOnly().renderer_hidden)
834 new_policy.rail_mode = v8::PERFORMANCE_IDLE;
835
836 if (expensive_task_policy == ExpensiveTaskPolicy::BLOCK &&
837 (!MainThreadOnly().expensive_task_blocking_allowed ||
838 !MainThreadOnly().have_seen_a_begin_main_frame ||
839 MainThreadOnly().navigation_task_expected_count > 0)) {
840 expensive_task_policy = ExpensiveTaskPolicy::RUN;
841 }
842
843 switch (expensive_task_policy) {
844 case ExpensiveTaskPolicy::RUN:
845 break;
846
847 case ExpensiveTaskPolicy::BLOCK:
848 if (loading_tasks_seem_expensive)
849 new_policy.loading_queue_policy.is_enabled = false;
850 if (timer_tasks_seem_expensive)
851 new_policy.timer_queue_policy.is_enabled = false;
852 break;
853
854 case ExpensiveTaskPolicy::THROTTLE:
855 if (loading_tasks_seem_expensive) {
856 new_policy.loading_queue_policy.time_domain_type =
857 TimeDomainType::THROTTLED;
858 }
859 if (timer_tasks_seem_expensive) {
860 new_policy.timer_queue_policy.time_domain_type =
861 TimeDomainType::THROTTLED;
862 }
863 break;
864 }
865
866 MainThreadOnly().expensive_task_policy = expensive_task_policy;
867
868 if (MainThreadOnly().timer_queue_suspend_count != 0 ||
869 MainThreadOnly().timer_queue_suspended_when_backgrounded) {
870 new_policy.timer_queue_policy.is_enabled = false;
871 new_policy.timer_queue_policy.time_domain_type = TimeDomainType::REAL;
872 }
873
874 if (MainThreadOnly().renderer_suspended) {
875 new_policy.loading_queue_policy.is_enabled = false;
876 DCHECK(!new_policy.timer_queue_policy.is_enabled);
877 }
878
879 // Tracing is done before the early out check, because it's quite possible we
880 // will otherwise miss this information in traces.
881 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
882 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
883 this, AsValueLocked(now));
884 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "use_case",
885 use_case);
886 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "rail_mode",
887 new_policy.rail_mode);
888 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
889 "touchstart_expected_soon",
890 MainThreadOnly().touchstart_expected_soon);
891 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
892 "expensive_task_policy", expensive_task_policy);
893 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
894 "RendererScheduler.loading_tasks_seem_expensive",
895 MainThreadOnly().loading_tasks_seem_expensive);
896 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
897 "RendererScheduler.timer_tasks_seem_expensive",
898 MainThreadOnly().timer_tasks_seem_expensive);
899
900 // TODO(alexclarke): Can we get rid of force update now?
901 if (update_type == UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED &&
902 new_policy == MainThreadOnly().current_policy) {
903 return;
904 }
905
906 ApplyTaskQueuePolicy(compositor_task_runner_.get(),
907 MainThreadOnly().current_policy.compositor_queue_policy,
908 new_policy.compositor_queue_policy);
909
910 for (const scoped_refptr<TaskQueue>& loading_queue : loading_task_runners_) {
911 ApplyTaskQueuePolicy(loading_queue.get(),
912 MainThreadOnly().current_policy.loading_queue_policy,
913 new_policy.loading_queue_policy);
914 }
915
916 for (const scoped_refptr<TaskQueue>& timer_queue : timer_task_runners_) {
917 ApplyTaskQueuePolicy(timer_queue.get(),
918 MainThreadOnly().current_policy.timer_queue_policy,
919 new_policy.timer_queue_policy);
920 }
921 MainThreadOnly().have_reported_blocking_intervention_in_current_policy =
922 false;
923
924 // TODO(alexclarke): We shouldn't have to prioritize the default queue, but it
925 // appears to be necessary since the order of loading tasks and IPCs (which
926 // are mostly dispatched on the default queue) need to be preserved.
927 ApplyTaskQueuePolicy(helper_.DefaultTaskRunner().get(),
928 MainThreadOnly().current_policy.default_queue_policy,
929 new_policy.default_queue_policy);
930 if (MainThreadOnly().rail_mode_observer &&
931 new_policy.rail_mode != MainThreadOnly().current_policy.rail_mode) {
932 MainThreadOnly().rail_mode_observer->OnRAILModeChanged(
933 new_policy.rail_mode);
934 }
935
936 DCHECK(compositor_task_runner_->IsQueueEnabled());
937 MainThreadOnly().current_policy = new_policy;
938 }
939
940 void RendererSchedulerImpl::ApplyTaskQueuePolicy(
941 TaskQueue* task_queue,
942 const TaskQueuePolicy& old_task_queue_policy,
943 const TaskQueuePolicy& new_task_queue_policy) const {
944 if (old_task_queue_policy.is_enabled != new_task_queue_policy.is_enabled) {
945 throttling_helper_->SetQueueEnabled(task_queue,
946 new_task_queue_policy.is_enabled);
947 }
948
949 if (old_task_queue_policy.priority != new_task_queue_policy.priority)
950 task_queue->SetQueuePriority(new_task_queue_policy.priority);
951
952 if (old_task_queue_policy.time_domain_type !=
953 new_task_queue_policy.time_domain_type) {
954 if (new_task_queue_policy.time_domain_type == TimeDomainType::THROTTLED) {
955 throttling_helper_->IncreaseThrottleRefCount(task_queue);
956 } else if (old_task_queue_policy.time_domain_type ==
957 TimeDomainType::THROTTLED) {
958 throttling_helper_->DecreaseThrottleRefCount(task_queue);
959 }
960 }
961 }
962
963 RendererSchedulerImpl::UseCase RendererSchedulerImpl::ComputeCurrentUseCase(
964 base::TimeTicks now,
965 base::TimeDelta* expected_use_case_duration) const {
966 any_thread_lock_.AssertAcquired();
967 // Special case for flings. This is needed because we don't get notification
968 // of a fling ending (although we do for cancellation).
969 if (AnyThread().fling_compositor_escalation_deadline > now &&
970 !AnyThread().awaiting_touch_start_response) {
971 *expected_use_case_duration =
972 AnyThread().fling_compositor_escalation_deadline - now;
973 return UseCase::COMPOSITOR_GESTURE;
974 }
975 // Above all else we want to be responsive to user input.
976 *expected_use_case_duration =
977 AnyThread().user_model.TimeLeftInUserGesture(now);
978 if (*expected_use_case_duration > base::TimeDelta()) {
979 // Has a gesture been fully established?
980 if (AnyThread().awaiting_touch_start_response) {
981 // No, so arrange for compositor tasks to be run at the highest priority.
982 return UseCase::TOUCHSTART;
983 }
984
985 // Yes a gesture has been established. Based on how the gesture is handled
986 // we need to choose between one of four use cases:
987 // 1. COMPOSITOR_GESTURE where the gesture is processed only on the
988 // compositor thread.
989 // 2. MAIN_THREAD_GESTURE where the gesture is processed only on the main
990 // thread.
991 // 3. MAIN_THREAD_CUSTOM_INPUT_HANDLING where the main thread processes a
992 // stream of input events and has prevented a default gesture from being
993 // started.
994 // 4. SYNCHRONIZED_GESTURE where the gesture is processed on both threads.
995 // TODO(skyostil): Consider removing in_idle_period_ and
996 // HadAnIdlePeriodRecently() unless we need them here.
997 if (AnyThread().last_gesture_was_compositor_driven) {
998 if (AnyThread().begin_main_frame_on_critical_path) {
999 return UseCase::SYNCHRONIZED_GESTURE;
1000 } else {
1001 return UseCase::COMPOSITOR_GESTURE;
1002 }
1003 }
1004 if (AnyThread().default_gesture_prevented) {
1005 return UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING;
1006 } else {
1007 return UseCase::MAIN_THREAD_GESTURE;
1008 }
1009 }
1010
1011 // TODO(alexclarke): return UseCase::LOADING if signals suggest the system is
1012 // in the initial 1s of RAIL loading.
1013 return UseCase::NONE;
1014 }
1015
1016 base::TimeDelta RendererSchedulerImpl::EstimateLongestJankFreeTaskDuration()
1017 const {
1018 switch (MainThreadOnly().current_use_case) {
1019 case UseCase::TOUCHSTART:
1020 case UseCase::COMPOSITOR_GESTURE:
1021 case UseCase::LOADING:
1022 case UseCase::NONE:
1023 return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis);
1024
1025 case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING:
1026 case UseCase::MAIN_THREAD_GESTURE:
1027 case UseCase::SYNCHRONIZED_GESTURE:
1028 return MainThreadOnly().idle_time_estimator.GetExpectedIdleDuration(
1029 MainThreadOnly().compositor_frame_interval);
1030
1031 default:
1032 NOTREACHED();
1033 return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis);
1034 }
1035 }
1036
1037 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
1038 base::TimeTicks now,
1039 base::TimeDelta* next_long_idle_period_delay_out) {
1040 helper_.CheckOnValidThread();
1041
1042 MaybeUpdatePolicy();
1043 if (MainThreadOnly().current_use_case == UseCase::TOUCHSTART) {
1044 // Don't start a long idle task in touch start priority, try again when
1045 // the policy is scheduled to end.
1046 *next_long_idle_period_delay_out =
1047 std::max(base::TimeDelta(),
1048 MainThreadOnly().current_policy_expiration_time - now);
1049 return false;
1050 }
1051 return true;
1052 }
1053
1054 SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
1055 return &helper_;
1056 }
1057
1058 TaskCostEstimator*
1059 RendererSchedulerImpl::GetLoadingTaskCostEstimatorForTesting() {
1060 return &MainThreadOnly().loading_task_cost_estimator;
1061 }
1062
1063 TaskCostEstimator*
1064 RendererSchedulerImpl::GetTimerTaskCostEstimatorForTesting() {
1065 return &MainThreadOnly().timer_task_cost_estimator;
1066 }
1067
1068 IdleTimeEstimator* RendererSchedulerImpl::GetIdleTimeEstimatorForTesting() {
1069 return &MainThreadOnly().idle_time_estimator;
1070 }
1071
1072 void RendererSchedulerImpl::SuspendTimerQueue() {
1073 MainThreadOnly().timer_queue_suspend_count++;
1074 ForceUpdatePolicy();
1075 #ifndef NDEBUG
1076 DCHECK(!default_timer_task_runner_->IsQueueEnabled());
1077 for (const auto& runner : timer_task_runners_) {
1078 DCHECK(!runner->IsQueueEnabled());
1079 }
1080 #endif
1081 }
1082
1083 void RendererSchedulerImpl::ResumeTimerQueue() {
1084 MainThreadOnly().timer_queue_suspend_count--;
1085 DCHECK_GE(MainThreadOnly().timer_queue_suspend_count, 0);
1086 ForceUpdatePolicy();
1087 }
1088
1089 void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled(
1090 bool enabled) {
1091 // Note that this will only take effect for the next backgrounded signal.
1092 MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled = enabled;
1093 }
1094
1095 std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
1096 RendererSchedulerImpl::AsValue(base::TimeTicks optional_now) const {
1097 base::AutoLock lock(any_thread_lock_);
1098 return AsValueLocked(optional_now);
1099 }
1100
1101 // static
1102 const char* RendererSchedulerImpl::ExpensiveTaskPolicyToString(
1103 ExpensiveTaskPolicy expensive_task_policy) {
1104 switch (expensive_task_policy) {
1105 case ExpensiveTaskPolicy::RUN:
1106 return "RUN";
1107 case ExpensiveTaskPolicy::BLOCK:
1108 return "BLOCK";
1109 case ExpensiveTaskPolicy::THROTTLE:
1110 return "THROTTLE";
1111 default:
1112 NOTREACHED();
1113 return nullptr;
1114 }
1115 }
1116
1117 std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
1118 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const {
1119 helper_.CheckOnValidThread();
1120 any_thread_lock_.AssertAcquired();
1121
1122 if (optional_now.is_null())
1123 optional_now = helper_.scheduler_tqm_delegate()->NowTicks();
1124 std::unique_ptr<base::trace_event::TracedValue> state(
1125 new base::trace_event::TracedValue());
1126 state->SetBoolean(
1127 "has_visible_render_widget_with_touch_handler",
1128 MainThreadOnly().has_visible_render_widget_with_touch_handler);
1129 state->SetString("current_use_case",
1130 UseCaseToString(MainThreadOnly().current_use_case));
1131 state->SetString("rail_mode",
1132 RAILModeToString(MainThreadOnly().current_policy.rail_mode));
1133 state->SetBoolean("expensive_task_blocking_allowed",
1134 MainThreadOnly().expensive_task_blocking_allowed);
1135 state->SetBoolean("loading_tasks_seem_expensive",
1136 MainThreadOnly().loading_tasks_seem_expensive);
1137 state->SetBoolean("timer_tasks_seem_expensive",
1138 MainThreadOnly().timer_tasks_seem_expensive);
1139 state->SetBoolean("begin_frame_not_expected_soon",
1140 MainThreadOnly().begin_frame_not_expected_soon);
1141 state->SetBoolean("touchstart_expected_soon",
1142 MainThreadOnly().touchstart_expected_soon);
1143 state->SetString("idle_period_state",
1144 IdleHelper::IdlePeriodStateToString(
1145 idle_helper_.SchedulerIdlePeriodState()));
1146 state->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden);
1147 state->SetBoolean("have_seen_a_begin_main_frame",
1148 MainThreadOnly().have_seen_a_begin_main_frame);
1149 state->SetBoolean(
1150 "have_reported_blocking_intervention_in_current_policy",
1151 MainThreadOnly().have_reported_blocking_intervention_in_current_policy);
1152 state->SetBoolean(
1153 "have_reported_blocking_intervention_since_navigation",
1154 MainThreadOnly().have_reported_blocking_intervention_since_navigation);
1155 state->SetBoolean("renderer_backgrounded",
1156 MainThreadOnly().renderer_backgrounded);
1157 state->SetBoolean("timer_queue_suspended_when_backgrounded",
1158 MainThreadOnly().timer_queue_suspended_when_backgrounded);
1159 state->SetInteger("timer_queue_suspend_count",
1160 MainThreadOnly().timer_queue_suspend_count);
1161 state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF());
1162 state->SetDouble(
1163 "rails_loading_priority_deadline",
1164 (AnyThread().rails_loading_priority_deadline - base::TimeTicks())
1165 .InMillisecondsF());
1166 state->SetDouble(
1167 "fling_compositor_escalation_deadline",
1168 (AnyThread().fling_compositor_escalation_deadline - base::TimeTicks())
1169 .InMillisecondsF());
1170 state->SetInteger("navigation_task_expected_count",
1171 MainThreadOnly().navigation_task_expected_count);
1172 state->SetDouble("last_idle_period_end_time",
1173 (AnyThread().last_idle_period_end_time - base::TimeTicks())
1174 .InMillisecondsF());
1175 state->SetBoolean("awaiting_touch_start_response",
1176 AnyThread().awaiting_touch_start_response);
1177 state->SetBoolean("begin_main_frame_on_critical_path",
1178 AnyThread().begin_main_frame_on_critical_path);
1179 state->SetBoolean("last_gesture_was_compositor_driven",
1180 AnyThread().last_gesture_was_compositor_driven);
1181 state->SetBoolean("default_gesture_prevented",
1182 AnyThread().default_gesture_prevented);
1183 state->SetDouble("expected_loading_task_duration",
1184 MainThreadOnly()
1185 .loading_task_cost_estimator.expected_task_duration()
1186 .InMillisecondsF());
1187 state->SetDouble("expected_timer_task_duration",
1188 MainThreadOnly()
1189 .timer_task_cost_estimator.expected_task_duration()
1190 .InMillisecondsF());
1191 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
1192 state->SetDouble(
1193 "longest_jank_free_task_duration",
1194 MainThreadOnly().longest_jank_free_task_duration.InMillisecondsF());
1195 state->SetDouble(
1196 "compositor_frame_interval",
1197 MainThreadOnly().compositor_frame_interval.InMillisecondsF());
1198 state->SetDouble(
1199 "estimated_next_frame_begin",
1200 (MainThreadOnly().estimated_next_frame_begin - base::TimeTicks())
1201 .InMillisecondsF());
1202 state->SetBoolean("in_idle_period", AnyThread().in_idle_period);
1203
1204 state->SetString(
1205 "expensive_task_policy",
1206 ExpensiveTaskPolicyToString(MainThreadOnly().expensive_task_policy));
1207
1208 AnyThread().user_model.AsValueInto(state.get());
1209 render_widget_scheduler_signals_.AsValueInto(state.get());
1210
1211 return std::move(state);
1212 }
1213
1214 void RendererSchedulerImpl::OnIdlePeriodStarted() {
1215 base::AutoLock lock(any_thread_lock_);
1216 AnyThread().in_idle_period = true;
1217 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
1218 }
1219
1220 void RendererSchedulerImpl::OnIdlePeriodEnded() {
1221 base::AutoLock lock(any_thread_lock_);
1222 AnyThread().last_idle_period_end_time =
1223 helper_.scheduler_tqm_delegate()->NowTicks();
1224 AnyThread().in_idle_period = false;
1225 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
1226 }
1227
1228 void RendererSchedulerImpl::AddPendingNavigation(
1229 blink::WebScheduler::NavigatingFrameType type) {
1230 helper_.CheckOnValidThread();
1231 if (type == blink::WebScheduler::NavigatingFrameType::kMainFrame) {
1232 MainThreadOnly().navigation_task_expected_count++;
1233 UpdatePolicy();
1234 }
1235 }
1236
1237 void RendererSchedulerImpl::RemovePendingNavigation(
1238 blink::WebScheduler::NavigatingFrameType type) {
1239 helper_.CheckOnValidThread();
1240 DCHECK_GT(MainThreadOnly().navigation_task_expected_count, 0);
1241 if (type == blink::WebScheduler::NavigatingFrameType::kMainFrame &&
1242 MainThreadOnly().navigation_task_expected_count > 0) {
1243 MainThreadOnly().navigation_task_expected_count--;
1244 UpdatePolicy();
1245 }
1246 }
1247
1248 void RendererSchedulerImpl::OnNavigationStarted() {
1249 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
1250 "RendererSchedulerImpl::OnNavigationStarted");
1251 base::AutoLock lock(any_thread_lock_);
1252 AnyThread().rails_loading_priority_deadline =
1253 helper_.scheduler_tqm_delegate()->NowTicks() +
1254 base::TimeDelta::FromMilliseconds(
1255 kRailsInitialLoadingPrioritizationMillis);
1256 ResetForNavigationLocked();
1257 }
1258
1259 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now) const {
1260 return (now - AnyThread().last_idle_period_end_time) <=
1261 base::TimeDelta::FromMilliseconds(
1262 kIdlePeriodStarvationThresholdMillis);
1263 }
1264
1265 void RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded() {
1266 DCHECK(MainThreadOnly().renderer_backgrounded);
1267 if (MainThreadOnly().timer_queue_suspended_when_backgrounded)
1268 return;
1269
1270 MainThreadOnly().timer_queue_suspended_when_backgrounded = true;
1271 ForceUpdatePolicy();
1272 }
1273
1274 void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() {
1275 DCHECK(!MainThreadOnly().renderer_backgrounded);
1276 if (!MainThreadOnly().timer_queue_suspended_when_backgrounded)
1277 return;
1278
1279 MainThreadOnly().timer_queue_suspended_when_backgrounded = false;
1280 ForceUpdatePolicy();
1281 }
1282
1283 void RendererSchedulerImpl::ResetForNavigationLocked() {
1284 helper_.CheckOnValidThread();
1285 any_thread_lock_.AssertAcquired();
1286 AnyThread().user_model.Reset(helper_.scheduler_tqm_delegate()->NowTicks());
1287 AnyThread().have_seen_touchstart = false;
1288 MainThreadOnly().loading_task_cost_estimator.Clear();
1289 MainThreadOnly().timer_task_cost_estimator.Clear();
1290 MainThreadOnly().idle_time_estimator.Clear();
1291 MainThreadOnly().have_seen_a_begin_main_frame = false;
1292 MainThreadOnly().have_reported_blocking_intervention_since_navigation = false;
1293 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
1294 }
1295
1296 void RendererSchedulerImpl::SetTopLevelBlameContext(
1297 base::trace_event::BlameContext* blame_context) {
1298 // Any task that runs in the default task runners belongs to the context of
1299 // all frames (as opposed to a particular frame). Note that the task itself
1300 // may still enter a more specific blame context if necessary.
1301 //
1302 // Per-frame task runners (loading, timers, etc.) are configured with a more
1303 // specific blame context by WebFrameSchedulerImpl.
1304 control_task_runner_->SetBlameContext(blame_context);
1305 DefaultTaskRunner()->SetBlameContext(blame_context);
1306 default_loading_task_runner_->SetBlameContext(blame_context);
1307 default_timer_task_runner_->SetBlameContext(blame_context);
1308 compositor_task_runner_->SetBlameContext(blame_context);
1309 idle_helper_.IdleTaskRunner()->SetBlameContext(blame_context);
1310 }
1311
1312 void RendererSchedulerImpl::SetRAILModeObserver(RAILModeObserver* observer) {
1313 MainThreadOnly().rail_mode_observer = observer;
1314 }
1315
1316 void RendererSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
1317 helper_.RegisterTimeDomain(time_domain);
1318 }
1319
1320 void RendererSchedulerImpl::UnregisterTimeDomain(TimeDomain* time_domain) {
1321 helper_.UnregisterTimeDomain(time_domain);
1322 }
1323
1324 void RendererSchedulerImpl::SetExpensiveTaskBlockingAllowed(bool allowed) {
1325 MainThreadOnly().expensive_task_blocking_allowed = allowed;
1326 }
1327
1328 base::TickClock* RendererSchedulerImpl::tick_clock() const {
1329 return helper_.scheduler_tqm_delegate().get();
1330 }
1331
1332 void RendererSchedulerImpl::AddWebViewScheduler(
1333 WebViewSchedulerImpl* web_view_scheduler) {
1334 MainThreadOnly().web_view_schedulers.insert(web_view_scheduler);
1335 }
1336
1337 void RendererSchedulerImpl::RemoveWebViewScheduler(
1338 WebViewSchedulerImpl* web_view_scheduler) {
1339 DCHECK(MainThreadOnly().web_view_schedulers.find(web_view_scheduler) !=
1340 MainThreadOnly().web_view_schedulers.end());
1341 MainThreadOnly().web_view_schedulers.erase(web_view_scheduler);
1342 }
1343
1344 void RendererSchedulerImpl::BroadcastConsoleWarning(
1345 const std::string& message) {
1346 helper_.CheckOnValidThread();
1347 for (auto& web_view_scheduler : MainThreadOnly().web_view_schedulers)
1348 web_view_scheduler->AddConsoleWarning(message);
1349 }
1350
1351 void RendererSchedulerImpl::OnTriedToExecuteBlockedTask(
1352 const TaskQueue& queue,
1353 const base::PendingTask& task) {
1354 if (!MainThreadOnly().expensive_task_blocking_allowed ||
1355 MainThreadOnly().current_use_case == UseCase::TOUCHSTART ||
1356 MainThreadOnly().longest_jank_free_task_duration <
1357 base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis) ||
1358 MainThreadOnly().timer_queue_suspend_count ||
1359 MainThreadOnly().timer_queue_suspended_when_backgrounded) {
1360 return;
1361 }
1362 if (!MainThreadOnly().timer_tasks_seem_expensive &&
1363 !MainThreadOnly().loading_tasks_seem_expensive) {
1364 return;
1365 }
1366 if (!MainThreadOnly().have_reported_blocking_intervention_in_current_policy) {
1367 MainThreadOnly().have_reported_blocking_intervention_in_current_policy =
1368 true;
1369 TRACE_EVENT_INSTANT0("renderer.scheduler",
1370 "RendererSchedulerImpl::TaskBlocked",
1371 TRACE_EVENT_SCOPE_THREAD);
1372 }
1373
1374 if (!MainThreadOnly().have_reported_blocking_intervention_since_navigation) {
1375 {
1376 base::AutoLock lock(any_thread_lock_);
1377 if (!AnyThread().have_seen_touchstart)
1378 return;
1379 }
1380 MainThreadOnly().have_reported_blocking_intervention_since_navigation =
1381 true;
1382 BroadcastConsoleWarning(
1383 "Blink deferred a task in order to make scrolling smoother. "
1384 "Your timer and network tasks should take less than 50ms to run "
1385 "to avoid this. Please see "
1386 "https://developers.google.com/web/tools/chrome-devtools/profile/evaluat e-performance/rail"
1387 " and https://crbug.com/574343#c40 for more information.");
1388 }
1389 }
1390
1391 void RendererSchedulerImpl::ReportTaskTime(base::TimeTicks start_time,
1392 base::TimeTicks end_time) {
1393 MainThreadOnly().queueing_time_estimator.OnToplevelTaskCompleted(start_time,
1394 end_time);
1395 }
1396
1397 void RendererSchedulerImpl::OnQueueingTimeForWindowEstimated(
1398 base::TimeDelta queueing_time) {
1399 UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration",
1400 queueing_time);
1401 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
1402 "estimated_queueing_time_for_window",
1403 queueing_time.InMillisecondsF());
1404 }
1405
1406 // static
1407 const char* RendererSchedulerImpl::UseCaseToString(UseCase use_case) {
1408 switch (use_case) {
1409 case UseCase::NONE:
1410 return "none";
1411 case UseCase::COMPOSITOR_GESTURE:
1412 return "compositor_gesture";
1413 case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING:
1414 return "main_thread_custom_input_handling";
1415 case UseCase::SYNCHRONIZED_GESTURE:
1416 return "synchronized_gesture";
1417 case UseCase::TOUCHSTART:
1418 return "touchstart";
1419 case UseCase::LOADING:
1420 return "loading";
1421 case UseCase::MAIN_THREAD_GESTURE:
1422 return "main_thread_gesture";
1423 default:
1424 NOTREACHED();
1425 return nullptr;
1426 }
1427 }
1428
1429 // static
1430 const char* RendererSchedulerImpl::RAILModeToString(v8::RAILMode rail_mode) {
1431 switch (rail_mode) {
1432 case v8::PERFORMANCE_RESPONSE:
1433 return "response";
1434 case v8::PERFORMANCE_ANIMATION:
1435 return "animation";
1436 case v8::PERFORMANCE_IDLE:
1437 return "idle";
1438 case v8::PERFORMANCE_LOAD:
1439 return "load";
1440 default:
1441 NOTREACHED();
1442 return nullptr;
1443 }
1444 }
1445
1446 } // namespace scheduler
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698