| 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/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 | |
| OLD | NEW |