| 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 } | |
| 1427 | |
| 1428 LongTaskTracker::LongTaskTiming RendererSchedulerImpl::GetLongTaskTiming() { | |
| 1429 return MainThreadOnly().long_task_tracker.GetLongTaskTiming(); | |
| 1430 } | |
| 1431 | |
| 1432 void RendererSchedulerImpl::OnQueueingTimeForWindowEstimated( | |
| 1433 base::TimeDelta queueing_time) { | |
| 1434 UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration", | |
| 1435 queueing_time); | |
| 1436 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
| 1437 "estimated_queueing_time_for_window", | |
| 1438 queueing_time.InMillisecondsF()); | |
| 1439 } | |
| 1440 | |
| 1441 AutoAdvancingVirtualTimeDomain* RendererSchedulerImpl::GetVirtualTimeDomain() { | |
| 1442 if (!virtual_time_domain_) { | |
| 1443 virtual_time_domain_.reset( | |
| 1444 new AutoAdvancingVirtualTimeDomain(tick_clock()->NowTicks())); | |
| 1445 RegisterTimeDomain(virtual_time_domain_.get()); | |
| 1446 } | |
| 1447 return virtual_time_domain_.get(); | |
| 1448 } | |
| 1449 | |
| 1450 void RendererSchedulerImpl::EnableVirtualTime() { | |
| 1451 MainThreadOnly().use_virtual_time = true; | |
| 1452 | |
| 1453 // The |unthrottled_task_runners_| are not actively managed by UpdatePolicy(). | |
| 1454 AutoAdvancingVirtualTimeDomain* time_domain = GetVirtualTimeDomain(); | |
| 1455 for (const scoped_refptr<TaskQueue>& task_queue : unthrottled_task_runners_) | |
| 1456 task_queue->SetTimeDomain(time_domain); | |
| 1457 | |
| 1458 throttling_helper_->EnableVirtualTime(); | |
| 1459 | |
| 1460 ForceUpdatePolicy(); | |
| 1461 } | |
| 1462 | |
| 1463 // static | |
| 1464 const char* RendererSchedulerImpl::UseCaseToString(UseCase use_case) { | |
| 1465 switch (use_case) { | |
| 1466 case UseCase::NONE: | |
| 1467 return "none"; | |
| 1468 case UseCase::COMPOSITOR_GESTURE: | |
| 1469 return "compositor_gesture"; | |
| 1470 case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING: | |
| 1471 return "main_thread_custom_input_handling"; | |
| 1472 case UseCase::SYNCHRONIZED_GESTURE: | |
| 1473 return "synchronized_gesture"; | |
| 1474 case UseCase::TOUCHSTART: | |
| 1475 return "touchstart"; | |
| 1476 case UseCase::LOADING: | |
| 1477 return "loading"; | |
| 1478 case UseCase::MAIN_THREAD_GESTURE: | |
| 1479 return "main_thread_gesture"; | |
| 1480 default: | |
| 1481 NOTREACHED(); | |
| 1482 return nullptr; | |
| 1483 } | |
| 1484 } | |
| 1485 | |
| 1486 // static | |
| 1487 const char* RendererSchedulerImpl::RAILModeToString(v8::RAILMode rail_mode) { | |
| 1488 switch (rail_mode) { | |
| 1489 case v8::PERFORMANCE_RESPONSE: | |
| 1490 return "response"; | |
| 1491 case v8::PERFORMANCE_ANIMATION: | |
| 1492 return "animation"; | |
| 1493 case v8::PERFORMANCE_IDLE: | |
| 1494 return "idle"; | |
| 1495 case v8::PERFORMANCE_LOAD: | |
| 1496 return "load"; | |
| 1497 default: | |
| 1498 NOTREACHED(); | |
| 1499 return nullptr; | |
| 1500 } | |
| 1501 } | |
| 1502 | |
| 1503 } // namespace scheduler | |
| OLD | NEW |