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