Index: components/scheduler/renderer/renderer_scheduler_impl.cc |
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc |
deleted file mode 100644 |
index 56e84f45ce23fba3dcd676c6a9f98fc236a72721..0000000000000000000000000000000000000000 |
--- a/components/scheduler/renderer/renderer_scheduler_impl.cc |
+++ /dev/null |
@@ -1,1506 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "components/scheduler/renderer/renderer_scheduler_impl.h" |
- |
-#include "base/bind.h" |
-#include "base/debug/stack_trace.h" |
-#include "base/logging.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/trace_event/trace_event.h" |
-#include "base/trace_event/trace_event_argument.h" |
-#include "cc/output/begin_frame_args.h" |
-#include "components/scheduler/base/task_queue_impl.h" |
-#include "components/scheduler/base/task_queue_selector.h" |
-#include "components/scheduler/base/virtual_time_domain.h" |
-#include "components/scheduler/child/scheduler_tqm_delegate.h" |
-#include "components/scheduler/renderer/auto_advancing_virtual_time_domain.h" |
-#include "components/scheduler/renderer/web_view_scheduler_impl.h" |
-#include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" |
- |
-namespace scheduler { |
-namespace { |
-// The run time of loading tasks is strongly bimodal. The vast majority are |
-// very cheap, but there are usually a handful of very expensive tasks (e.g ~1 |
-// second on a mobile device) so we take a very pessimistic view when estimating |
-// the cost of loading tasks. |
-const int kLoadingTaskEstimationSampleCount = 1000; |
-const double kLoadingTaskEstimationPercentile = 99; |
-const int kTimerTaskEstimationSampleCount = 1000; |
-const double kTimerTaskEstimationPercentile = 99; |
-const int kShortIdlePeriodDurationSampleCount = 10; |
-const double kShortIdlePeriodDurationPercentile = 50; |
-// Amount of idle time left in a frame (as a ratio of the vsync interval) above |
-// which main thread compositing can be considered fast. |
-const double kFastCompositingIdleTimeThreshold = .2; |
-} // namespace |
- |
-RendererSchedulerImpl::RendererSchedulerImpl( |
- scoped_refptr<SchedulerTqmDelegate> main_task_runner) |
- : helper_(main_task_runner, |
- "renderer.scheduler", |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")), |
- idle_helper_(&helper_, |
- this, |
- "renderer.scheduler", |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerIdlePeriod", |
- base::TimeDelta()), |
- render_widget_scheduler_signals_(this), |
- control_task_runner_(helper_.ControlTaskRunner()), |
- compositor_task_runner_(helper_.NewTaskQueue( |
- TaskQueue::Spec("compositor_tq").SetShouldMonitorQuiescence(true))), |
- delayed_update_policy_runner_( |
- base::Bind(&RendererSchedulerImpl::UpdatePolicy, |
- base::Unretained(this)), |
- helper_.ControlTaskRunner()), |
- main_thread_only_(this, |
- compositor_task_runner_, |
- helper_.scheduler_tqm_delegate().get()), |
- policy_may_need_update_(&any_thread_lock_), |
- weak_factory_(this) { |
- throttling_helper_.reset(new ThrottlingHelper(this, "renderer.scheduler")); |
- update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy, |
- weak_factory_.GetWeakPtr()); |
- end_renderer_hidden_idle_period_closure_.Reset(base::Bind( |
- &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr())); |
- |
- suspend_timers_when_backgrounded_closure_.Reset( |
- base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded, |
- weak_factory_.GetWeakPtr())); |
- |
- default_loading_task_runner_ = NewLoadingTaskRunner("default_loading_tq"); |
- default_timer_task_runner_ = NewTimerTaskRunner("default_timer_tq"); |
- |
- TRACE_EVENT_OBJECT_CREATED_WITH_ID( |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", |
- this); |
- |
- helper_.SetObserver(this); |
- helper_.SetTaskTimeTracker(this); |
-} |
- |
-RendererSchedulerImpl::~RendererSchedulerImpl() { |
- TRACE_EVENT_OBJECT_DELETED_WITH_ID( |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", |
- this); |
- |
- for (const scoped_refptr<TaskQueue>& loading_queue : loading_task_runners_) { |
- loading_queue->RemoveTaskObserver( |
- &MainThreadOnly().loading_task_cost_estimator); |
- } |
- for (const scoped_refptr<TaskQueue>& timer_queue : timer_task_runners_) { |
- timer_queue->RemoveTaskObserver( |
- &MainThreadOnly().timer_task_cost_estimator); |
- } |
- |
- if (virtual_time_domain_) |
- UnregisterTimeDomain(virtual_time_domain_.get()); |
- |
- // Ensure the renderer scheduler was shut down explicitly, because otherwise |
- // we could end up having stale pointers to the Blink heap which has been |
- // terminated by this point. |
- DCHECK(MainThreadOnly().was_shutdown); |
-} |
- |
-RendererSchedulerImpl::MainThreadOnly::MainThreadOnly( |
- RendererSchedulerImpl* renderer_scheduler_impl, |
- const scoped_refptr<TaskQueue>& compositor_task_runner, |
- base::TickClock* time_source) |
- : loading_task_cost_estimator(time_source, |
- kLoadingTaskEstimationSampleCount, |
- kLoadingTaskEstimationPercentile), |
- timer_task_cost_estimator(time_source, |
- kTimerTaskEstimationSampleCount, |
- kTimerTaskEstimationPercentile), |
- queueing_time_estimator(renderer_scheduler_impl, |
- base::TimeDelta::FromSeconds(1)), |
- idle_time_estimator(compositor_task_runner, |
- time_source, |
- kShortIdlePeriodDurationSampleCount, |
- kShortIdlePeriodDurationPercentile), |
- current_use_case(UseCase::NONE), |
- timer_queue_suspend_count(0), |
- navigation_task_expected_count(0), |
- expensive_task_policy(ExpensiveTaskPolicy::RUN), |
- renderer_hidden(false), |
- renderer_backgrounded(false), |
- renderer_suspended(false), |
- timer_queue_suspension_when_backgrounded_enabled(false), |
- timer_queue_suspended_when_backgrounded(false), |
- was_shutdown(false), |
- loading_tasks_seem_expensive(false), |
- timer_tasks_seem_expensive(false), |
- touchstart_expected_soon(false), |
- have_seen_a_begin_main_frame(false), |
- have_reported_blocking_intervention_in_current_policy(false), |
- have_reported_blocking_intervention_since_navigation(false), |
- has_visible_render_widget_with_touch_handler(false), |
- begin_frame_not_expected_soon(false), |
- expensive_task_blocking_allowed(true), |
- in_idle_period_for_testing(false), |
- use_virtual_time(false), |
- rail_mode_observer(nullptr) {} |
- |
-RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {} |
- |
-RendererSchedulerImpl::AnyThread::AnyThread() |
- : awaiting_touch_start_response(false), |
- in_idle_period(false), |
- begin_main_frame_on_critical_path(false), |
- last_gesture_was_compositor_driven(false), |
- default_gesture_prevented(true), |
- have_seen_touchstart(false) {} |
- |
-RendererSchedulerImpl::AnyThread::~AnyThread() {} |
- |
-RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly() |
- : last_input_type(blink::WebInputEvent::Undefined) {} |
- |
-RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {} |
- |
-void RendererSchedulerImpl::Shutdown() { |
- throttling_helper_.reset(); |
- helper_.Shutdown(); |
- MainThreadOnly().was_shutdown = true; |
- MainThreadOnly().rail_mode_observer = nullptr; |
-} |
- |
-std::unique_ptr<blink::WebThread> RendererSchedulerImpl::CreateMainThread() { |
- return base::WrapUnique(new WebThreadImplForRendererScheduler(this)); |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::DefaultTaskRunner() { |
- return helper_.DefaultTaskRunner(); |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::CompositorTaskRunner() { |
- helper_.CheckOnValidThread(); |
- return compositor_task_runner_; |
-} |
- |
-scoped_refptr<SingleThreadIdleTaskRunner> |
-RendererSchedulerImpl::IdleTaskRunner() { |
- return idle_helper_.IdleTaskRunner(); |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::LoadingTaskRunner() { |
- helper_.CheckOnValidThread(); |
- return default_loading_task_runner_; |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::TimerTaskRunner() { |
- helper_.CheckOnValidThread(); |
- return default_timer_task_runner_; |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::ControlTaskRunner() { |
- helper_.CheckOnValidThread(); |
- return helper_.ControlTaskRunner(); |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::NewLoadingTaskRunner( |
- const char* name) { |
- helper_.CheckOnValidThread(); |
- scoped_refptr<TaskQueue> loading_task_queue(helper_.NewTaskQueue( |
- TaskQueue::Spec(name).SetShouldMonitorQuiescence(true).SetTimeDomain( |
- MainThreadOnly().use_virtual_time ? GetVirtualTimeDomain() |
- : nullptr))); |
- loading_task_runners_.insert(loading_task_queue); |
- loading_task_queue->SetQueueEnabled( |
- MainThreadOnly().current_policy.loading_queue_policy.is_enabled); |
- loading_task_queue->SetQueuePriority( |
- MainThreadOnly().current_policy.loading_queue_policy.priority); |
- if (MainThreadOnly().current_policy.loading_queue_policy.time_domain_type == |
- TimeDomainType::THROTTLED) { |
- throttling_helper_->IncreaseThrottleRefCount(loading_task_queue.get()); |
- } |
- loading_task_queue->AddTaskObserver( |
- &MainThreadOnly().loading_task_cost_estimator); |
- return loading_task_queue; |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::NewTimerTaskRunner( |
- const char* name) { |
- helper_.CheckOnValidThread(); |
- // TODO(alexclarke): Consider using ApplyTaskQueuePolicy() for brevity. |
- scoped_refptr<TaskQueue> timer_task_queue( |
- helper_.NewTaskQueue(TaskQueue::Spec(name) |
- .SetShouldMonitorQuiescence(true) |
- .SetShouldReportWhenExecutionBlocked(true) |
- .SetTimeDomain(MainThreadOnly().use_virtual_time |
- ? GetVirtualTimeDomain() |
- : nullptr))); |
- timer_task_runners_.insert(timer_task_queue); |
- timer_task_queue->SetQueueEnabled( |
- MainThreadOnly().current_policy.timer_queue_policy.is_enabled); |
- timer_task_queue->SetQueuePriority( |
- MainThreadOnly().current_policy.timer_queue_policy.priority); |
- if (MainThreadOnly().current_policy.timer_queue_policy.time_domain_type == |
- TimeDomainType::THROTTLED) { |
- throttling_helper_->IncreaseThrottleRefCount(timer_task_queue.get()); |
- } |
- timer_task_queue->AddTaskObserver( |
- &MainThreadOnly().timer_task_cost_estimator); |
- return timer_task_queue; |
-} |
- |
-scoped_refptr<TaskQueue> RendererSchedulerImpl::NewUnthrottledTaskRunner( |
- const char* name) { |
- helper_.CheckOnValidThread(); |
- scoped_refptr<TaskQueue> unthrottled_task_queue(helper_.NewTaskQueue( |
- TaskQueue::Spec(name).SetShouldMonitorQuiescence(true).SetTimeDomain( |
- MainThreadOnly().use_virtual_time ? GetVirtualTimeDomain() |
- : nullptr))); |
- unthrottled_task_runners_.insert(unthrottled_task_queue); |
- return unthrottled_task_queue; |
-} |
- |
-std::unique_ptr<RenderWidgetSchedulingState> |
-RendererSchedulerImpl::NewRenderWidgetSchedulingState() { |
- return render_widget_scheduler_signals_.NewRenderWidgetSchedulingState(); |
-} |
- |
-void RendererSchedulerImpl::OnUnregisterTaskQueue( |
- const scoped_refptr<TaskQueue>& task_queue) { |
- if (throttling_helper_.get()) |
- throttling_helper_->UnregisterTaskQueue(task_queue.get()); |
- |
- if (loading_task_runners_.find(task_queue) != loading_task_runners_.end()) { |
- task_queue->RemoveTaskObserver( |
- &MainThreadOnly().loading_task_cost_estimator); |
- loading_task_runners_.erase(task_queue); |
- } else if (timer_task_runners_.find(task_queue) != |
- timer_task_runners_.end()) { |
- task_queue->RemoveTaskObserver(&MainThreadOnly().timer_task_cost_estimator); |
- timer_task_runners_.erase(task_queue); |
- } else if (unthrottled_task_runners_.find(task_queue) != |
- unthrottled_task_runners_.end()) { |
- unthrottled_task_runners_.erase(task_queue); |
- } |
-} |
- |
-bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const { |
- return idle_helper_.CanExceedIdleDeadlineIfRequired(); |
-} |
- |
-void RendererSchedulerImpl::AddTaskObserver( |
- base::MessageLoop::TaskObserver* task_observer) { |
- helper_.AddTaskObserver(task_observer); |
-} |
- |
-void RendererSchedulerImpl::RemoveTaskObserver( |
- base::MessageLoop::TaskObserver* task_observer) { |
- helper_.RemoveTaskObserver(task_observer); |
-} |
- |
-void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs& args) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::WillBeginFrame", "args", args.AsValue()); |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown()) |
- return; |
- |
- EndIdlePeriod(); |
- MainThreadOnly().estimated_next_frame_begin = args.frame_time + args.interval; |
- MainThreadOnly().have_seen_a_begin_main_frame = true; |
- MainThreadOnly().begin_frame_not_expected_soon = false; |
- MainThreadOnly().compositor_frame_interval = args.interval; |
- { |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().begin_main_frame_on_critical_path = args.on_critical_path; |
- } |
-} |
- |
-void RendererSchedulerImpl::DidCommitFrameToCompositor() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::DidCommitFrameToCompositor"); |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown()) |
- return; |
- |
- base::TimeTicks now(helper_.scheduler_tqm_delegate()->NowTicks()); |
- if (now < MainThreadOnly().estimated_next_frame_begin) { |
- // TODO(rmcilroy): Consider reducing the idle period based on the runtime of |
- // the next pending delayed tasks (as currently done in for long idle times) |
- idle_helper_.StartIdlePeriod( |
- IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now, |
- MainThreadOnly().estimated_next_frame_begin); |
- } |
- |
- MainThreadOnly().idle_time_estimator.DidCommitFrameToCompositor(); |
-} |
- |
-void RendererSchedulerImpl::BeginFrameNotExpectedSoon() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::BeginFrameNotExpectedSoon"); |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown()) |
- return; |
- |
- MainThreadOnly().begin_frame_not_expected_soon = true; |
- idle_helper_.EnableLongIdlePeriod(); |
- { |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().begin_main_frame_on_critical_path = false; |
- } |
-} |
- |
-void RendererSchedulerImpl::SetAllRenderWidgetsHidden(bool hidden) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::SetAllRenderWidgetsHidden", "hidden", |
- hidden); |
- |
- helper_.CheckOnValidThread(); |
- |
- if (helper_.IsShutdown() || MainThreadOnly().renderer_hidden == hidden) |
- return; |
- |
- end_renderer_hidden_idle_period_closure_.Cancel(); |
- |
- if (hidden) { |
- idle_helper_.EnableLongIdlePeriod(); |
- |
- // Ensure that we stop running idle tasks after a few seconds of being |
- // hidden. |
- base::TimeDelta end_idle_when_hidden_delay = |
- base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis); |
- control_task_runner_->PostDelayedTask( |
- FROM_HERE, end_renderer_hidden_idle_period_closure_.callback(), |
- end_idle_when_hidden_delay); |
- MainThreadOnly().renderer_hidden = true; |
- } else { |
- MainThreadOnly().renderer_hidden = false; |
- EndIdlePeriod(); |
- } |
- |
- // TODO(alexclarke): Should we update policy here? |
- TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", |
- this, AsValue(helper_.scheduler_tqm_delegate()->NowTicks())); |
-} |
- |
-void RendererSchedulerImpl::SetHasVisibleRenderWidgetWithTouchHandler( |
- bool has_visible_render_widget_with_touch_handler) { |
- helper_.CheckOnValidThread(); |
- if (has_visible_render_widget_with_touch_handler == |
- MainThreadOnly().has_visible_render_widget_with_touch_handler) |
- return; |
- |
- MainThreadOnly().has_visible_render_widget_with_touch_handler = |
- has_visible_render_widget_with_touch_handler; |
- |
- base::AutoLock lock(any_thread_lock_); |
- UpdatePolicyLocked(UpdateType::FORCE_UPDATE); |
-} |
- |
-void RendererSchedulerImpl::OnRendererBackgrounded() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::OnRendererBackgrounded"); |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown() || MainThreadOnly().renderer_backgrounded) |
- return; |
- |
- MainThreadOnly().renderer_backgrounded = true; |
- if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled) |
- return; |
- |
- suspend_timers_when_backgrounded_closure_.Cancel(); |
- base::TimeDelta suspend_timers_when_backgrounded_delay = |
- base::TimeDelta::FromMilliseconds( |
- kSuspendTimersWhenBackgroundedDelayMillis); |
- control_task_runner_->PostDelayedTask( |
- FROM_HERE, suspend_timers_when_backgrounded_closure_.callback(), |
- suspend_timers_when_backgrounded_delay); |
-} |
- |
-void RendererSchedulerImpl::OnRendererForegrounded() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::OnRendererForegrounded"); |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown() || !MainThreadOnly().renderer_backgrounded) |
- return; |
- |
- MainThreadOnly().renderer_backgrounded = false; |
- MainThreadOnly().renderer_suspended = false; |
- suspend_timers_when_backgrounded_closure_.Cancel(); |
- ResumeTimerQueueWhenForegrounded(); |
-} |
- |
-void RendererSchedulerImpl::SuspendRenderer() { |
- helper_.CheckOnValidThread(); |
- DCHECK(MainThreadOnly().renderer_backgrounded); |
- if (helper_.IsShutdown()) |
- return; |
- suspend_timers_when_backgrounded_closure_.Cancel(); |
- // TODO(hajimehoshi): We might need to suspend not only timer queue but also |
- // e.g. loading tasks or postMessage. |
- MainThreadOnly().renderer_suspended = true; |
- SuspendTimerQueueWhenBackgrounded(); |
-} |
- |
-void RendererSchedulerImpl::EndIdlePeriod() { |
- if (MainThreadOnly().in_idle_period_for_testing) |
- return; |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::EndIdlePeriod"); |
- helper_.CheckOnValidThread(); |
- idle_helper_.EndIdlePeriod(); |
-} |
- |
-void RendererSchedulerImpl::EndIdlePeriodForTesting( |
- const base::Closure& callback, |
- base::TimeTicks time_remaining) { |
- MainThreadOnly().in_idle_period_for_testing = false; |
- EndIdlePeriod(); |
- callback.Run(); |
-} |
- |
-bool RendererSchedulerImpl::PolicyNeedsUpdateForTesting() { |
- return policy_may_need_update_.IsSet(); |
-} |
- |
-// static |
-bool RendererSchedulerImpl::ShouldPrioritizeInputEvent( |
- const blink::WebInputEvent& web_input_event) { |
- // We regard MouseMove events with the left mouse button down as a signal |
- // that the user is doing something requiring a smooth frame rate. |
- if (web_input_event.type == blink::WebInputEvent::MouseMove && |
- (web_input_event.modifiers & blink::WebInputEvent::LeftButtonDown)) { |
- return true; |
- } |
- // Ignore all other mouse events because they probably don't signal user |
- // interaction needing a smooth framerate. NOTE isMouseEventType returns false |
- // for mouse wheel events, hence we regard them as user input. |
- // Ignore keyboard events because it doesn't really make sense to enter |
- // compositor priority for them. |
- if (blink::WebInputEvent::isMouseEventType(web_input_event.type) || |
- blink::WebInputEvent::isKeyboardEventType(web_input_event.type)) { |
- return false; |
- } |
- return true; |
-} |
- |
-void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread( |
- const blink::WebInputEvent& web_input_event, |
- InputEventState event_state) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread"); |
- if (!ShouldPrioritizeInputEvent(web_input_event)) |
- return; |
- |
- UpdateForInputEventOnCompositorThread(web_input_event.type, event_state); |
-} |
- |
-void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread"); |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().fling_compositor_escalation_deadline = |
- helper_.scheduler_tqm_delegate()->NowTicks() + |
- base::TimeDelta::FromMilliseconds(kFlingEscalationLimitMillis); |
-} |
- |
-void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread( |
- blink::WebInputEvent::Type type, |
- InputEventState input_event_state) { |
- base::AutoLock lock(any_thread_lock_); |
- base::TimeTicks now = helper_.scheduler_tqm_delegate()->NowTicks(); |
- |
- // TODO(alexclarke): Move WebInputEventTraits where we can access it from here |
- // and record the name rather than the integer representation. |
- TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread", |
- "type", static_cast<int>(type), "input_event_state", |
- InputEventStateToString(input_event_state)); |
- |
- base::TimeDelta unused_policy_duration; |
- UseCase previous_use_case = |
- ComputeCurrentUseCase(now, &unused_policy_duration); |
- bool was_awaiting_touch_start_response = |
- AnyThread().awaiting_touch_start_response; |
- |
- AnyThread().user_model.DidStartProcessingInputEvent(type, now); |
- |
- if (input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR) |
- AnyThread().user_model.DidFinishProcessingInputEvent(now); |
- |
- if (type) { |
- switch (type) { |
- case blink::WebInputEvent::TouchStart: |
- AnyThread().awaiting_touch_start_response = true; |
- // This is just a fail-safe to reset the state of |
- // |last_gesture_was_compositor_driven| to the default. We don't know |
- // yet where the gesture will run. |
- AnyThread().last_gesture_was_compositor_driven = false; |
- AnyThread().have_seen_touchstart = true; |
- // Assume the default gesture is prevented until we see evidence |
- // otherwise. |
- AnyThread().default_gesture_prevented = true; |
- break; |
- |
- case blink::WebInputEvent::TouchMove: |
- // Observation of consecutive touchmoves is a strong signal that the |
- // page is consuming the touch sequence, in which case touchstart |
- // response prioritization is no longer necessary. Otherwise, the |
- // initial touchmove should preserve the touchstart response pending |
- // state. |
- if (AnyThread().awaiting_touch_start_response && |
- CompositorThreadOnly().last_input_type == |
- blink::WebInputEvent::TouchMove) { |
- AnyThread().awaiting_touch_start_response = false; |
- } |
- break; |
- |
- case blink::WebInputEvent::GesturePinchUpdate: |
- case blink::WebInputEvent::GestureScrollUpdate: |
- // If we see events for an established gesture, we can lock it to the |
- // appropriate thread as the gesture can no longer be cancelled. |
- AnyThread().last_gesture_was_compositor_driven = |
- input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR; |
- AnyThread().awaiting_touch_start_response = false; |
- AnyThread().default_gesture_prevented = false; |
- break; |
- |
- case blink::WebInputEvent::GestureFlingCancel: |
- AnyThread().fling_compositor_escalation_deadline = base::TimeTicks(); |
- break; |
- |
- case blink::WebInputEvent::GestureTapDown: |
- case blink::WebInputEvent::GestureShowPress: |
- case blink::WebInputEvent::GestureScrollEnd: |
- // With no observable effect, these meta events do not indicate a |
- // meaningful touchstart response and should not impact task priority. |
- break; |
- |
- default: |
- AnyThread().awaiting_touch_start_response = false; |
- break; |
- } |
- } |
- |
- // Avoid unnecessary policy updates if the use case did not change. |
- UseCase use_case = ComputeCurrentUseCase(now, &unused_policy_duration); |
- |
- if (use_case != previous_use_case || |
- was_awaiting_touch_start_response != |
- AnyThread().awaiting_touch_start_response) { |
- EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE); |
- } |
- CompositorThreadOnly().last_input_type = type; |
-} |
- |
-void RendererSchedulerImpl::DidHandleInputEventOnMainThread( |
- const blink::WebInputEvent& web_input_event) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::DidHandleInputEventOnMainThread"); |
- helper_.CheckOnValidThread(); |
- if (ShouldPrioritizeInputEvent(web_input_event)) { |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().user_model.DidFinishProcessingInputEvent( |
- helper_.scheduler_tqm_delegate()->NowTicks()); |
- } |
-} |
- |
-bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() { |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown()) |
- return false; |
- |
- MaybeUpdatePolicy(); |
- // The touchstart, synchronized gesture and main-thread gesture use cases |
- // indicate a strong likelihood of high-priority work in the near future. |
- UseCase use_case = MainThreadOnly().current_use_case; |
- return MainThreadOnly().touchstart_expected_soon || |
- use_case == UseCase::TOUCHSTART || |
- use_case == UseCase::MAIN_THREAD_GESTURE || |
- use_case == UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING || |
- use_case == UseCase::SYNCHRONIZED_GESTURE; |
-} |
- |
-bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() { |
- helper_.CheckOnValidThread(); |
- if (helper_.IsShutdown()) |
- return false; |
- |
- MaybeUpdatePolicy(); |
- // We only yield if there's a urgent task to be run now, or we are expecting |
- // one soon (touch start). |
- // Note: even though the control queue has the highest priority we don't yield |
- // for it since these tasks are not user-provided work and they are only |
- // intended to run before the next task, not interrupt the tasks. |
- switch (MainThreadOnly().current_use_case) { |
- case UseCase::COMPOSITOR_GESTURE: |
- case UseCase::NONE: |
- return MainThreadOnly().touchstart_expected_soon; |
- |
- case UseCase::MAIN_THREAD_GESTURE: |
- case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING: |
- case UseCase::SYNCHRONIZED_GESTURE: |
- return compositor_task_runner_->HasPendingImmediateWork() || |
- MainThreadOnly().touchstart_expected_soon; |
- |
- case UseCase::TOUCHSTART: |
- return true; |
- |
- case UseCase::LOADING: |
- return false; |
- |
- default: |
- NOTREACHED(); |
- return false; |
- } |
-} |
- |
-base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting() |
- const { |
- return idle_helper_.CurrentIdleTaskDeadline(); |
-} |
- |
-void RendererSchedulerImpl::RunIdleTasksForTesting( |
- const base::Closure& callback) { |
- MainThreadOnly().in_idle_period_for_testing = true; |
- IdleTaskRunner()->PostIdleTask( |
- FROM_HERE, |
- base::Bind(&RendererSchedulerImpl::EndIdlePeriodForTesting, |
- weak_factory_.GetWeakPtr(), callback)); |
- idle_helper_.EnableLongIdlePeriod(); |
-} |
- |
-void RendererSchedulerImpl::MaybeUpdatePolicy() { |
- helper_.CheckOnValidThread(); |
- if (policy_may_need_update_.IsSet()) { |
- UpdatePolicy(); |
- } |
-} |
- |
-void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread( |
- const tracked_objects::Location& from_here) { |
- // TODO(scheduler-dev): Check that this method isn't called from the main |
- // thread. |
- any_thread_lock_.AssertAcquired(); |
- if (!policy_may_need_update_.IsSet()) { |
- policy_may_need_update_.SetWhileLocked(true); |
- control_task_runner_->PostTask(from_here, update_policy_closure_); |
- } |
-} |
- |
-void RendererSchedulerImpl::UpdatePolicy() { |
- base::AutoLock lock(any_thread_lock_); |
- UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
-} |
- |
-void RendererSchedulerImpl::ForceUpdatePolicy() { |
- base::AutoLock lock(any_thread_lock_); |
- UpdatePolicyLocked(UpdateType::FORCE_UPDATE); |
-} |
- |
-void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { |
- helper_.CheckOnValidThread(); |
- any_thread_lock_.AssertAcquired(); |
- if (helper_.IsShutdown()) |
- return; |
- |
- base::TimeTicks now = helper_.scheduler_tqm_delegate()->NowTicks(); |
- policy_may_need_update_.SetWhileLocked(false); |
- |
- base::TimeDelta expected_use_case_duration; |
- UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration); |
- MainThreadOnly().current_use_case = use_case; |
- |
- base::TimeDelta touchstart_expected_flag_valid_for_duration; |
- bool touchstart_expected_soon = false; |
- if (MainThreadOnly().has_visible_render_widget_with_touch_handler) { |
- touchstart_expected_soon = AnyThread().user_model.IsGestureExpectedSoon( |
- now, &touchstart_expected_flag_valid_for_duration); |
- } |
- MainThreadOnly().touchstart_expected_soon = touchstart_expected_soon; |
- |
- base::TimeDelta longest_jank_free_task_duration = |
- EstimateLongestJankFreeTaskDuration(); |
- MainThreadOnly().longest_jank_free_task_duration = |
- longest_jank_free_task_duration; |
- |
- bool loading_tasks_seem_expensive = false; |
- bool timer_tasks_seem_expensive = false; |
- loading_tasks_seem_expensive = |
- MainThreadOnly().loading_task_cost_estimator.expected_task_duration() > |
- longest_jank_free_task_duration; |
- timer_tasks_seem_expensive = |
- MainThreadOnly().timer_task_cost_estimator.expected_task_duration() > |
- longest_jank_free_task_duration; |
- MainThreadOnly().timer_tasks_seem_expensive = timer_tasks_seem_expensive; |
- MainThreadOnly().loading_tasks_seem_expensive = loading_tasks_seem_expensive; |
- |
- // The |new_policy_duration| is the minimum of |expected_use_case_duration| |
- // and |touchstart_expected_flag_valid_for_duration| unless one is zero in |
- // which case we choose the other. |
- base::TimeDelta new_policy_duration = expected_use_case_duration; |
- if (new_policy_duration.is_zero() || |
- (touchstart_expected_flag_valid_for_duration > base::TimeDelta() && |
- new_policy_duration > touchstart_expected_flag_valid_for_duration)) { |
- new_policy_duration = touchstart_expected_flag_valid_for_duration; |
- } |
- |
- if (new_policy_duration > base::TimeDelta()) { |
- MainThreadOnly().current_policy_expiration_time = now + new_policy_duration; |
- delayed_update_policy_runner_.SetDeadline(FROM_HERE, new_policy_duration, |
- now); |
- } else { |
- MainThreadOnly().current_policy_expiration_time = base::TimeTicks(); |
- } |
- |
- // Avoid prioritizing main thread compositing (e.g., rAF) if it is extremely |
- // slow, because that can cause starvation in other task sources. |
- bool main_thread_compositing_is_fast = |
- MainThreadOnly().idle_time_estimator.GetExpectedIdleDuration( |
- MainThreadOnly().compositor_frame_interval) > |
- MainThreadOnly().compositor_frame_interval * |
- kFastCompositingIdleTimeThreshold; |
- |
- Policy new_policy; |
- ExpensiveTaskPolicy expensive_task_policy = ExpensiveTaskPolicy::RUN; |
- new_policy.rail_mode = v8::PERFORMANCE_ANIMATION; |
- |
- switch (use_case) { |
- case UseCase::COMPOSITOR_GESTURE: |
- if (touchstart_expected_soon) { |
- new_policy.rail_mode = v8::PERFORMANCE_RESPONSE; |
- expensive_task_policy = ExpensiveTaskPolicy::BLOCK; |
- new_policy.compositor_queue_policy.priority = TaskQueue::HIGH_PRIORITY; |
- } else { |
- // What we really want to do is priorize loading tasks, but that doesn't |
- // seem to be safe. Instead we do that by proxy by deprioritizing |
- // compositor tasks. This should be safe since we've already gone to the |
- // pain of fixing ordering issues with them. |
- new_policy.compositor_queue_policy.priority = |
- TaskQueue::BEST_EFFORT_PRIORITY; |
- } |
- break; |
- |
- case UseCase::SYNCHRONIZED_GESTURE: |
- new_policy.compositor_queue_policy.priority = |
- main_thread_compositing_is_fast ? TaskQueue::HIGH_PRIORITY |
- : TaskQueue::NORMAL_PRIORITY; |
- if (touchstart_expected_soon) { |
- new_policy.rail_mode = v8::PERFORMANCE_RESPONSE; |
- expensive_task_policy = ExpensiveTaskPolicy::BLOCK; |
- } else { |
- expensive_task_policy = ExpensiveTaskPolicy::THROTTLE; |
- } |
- break; |
- |
- case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING: |
- // In main thread input handling scenarios we don't have perfect knowledge |
- // about which things we should be prioritizing, so we don't attempt to |
- // block expensive tasks because we don't know whether they were integral |
- // to the page's functionality or not. |
- new_policy.compositor_queue_policy.priority = |
- main_thread_compositing_is_fast ? TaskQueue::HIGH_PRIORITY |
- : TaskQueue::NORMAL_PRIORITY; |
- break; |
- |
- case UseCase::MAIN_THREAD_GESTURE: |
- // A main thread gesture is for example a scroll gesture which is handled |
- // by the main thread. Since we know the established gesture type, we can |
- // be a little more aggressive about prioritizing compositing and input |
- // handling over other tasks. |
- new_policy.compositor_queue_policy.priority = TaskQueue::HIGH_PRIORITY; |
- if (touchstart_expected_soon) { |
- new_policy.rail_mode = v8::PERFORMANCE_RESPONSE; |
- expensive_task_policy = ExpensiveTaskPolicy::BLOCK; |
- } else { |
- expensive_task_policy = ExpensiveTaskPolicy::THROTTLE; |
- } |
- break; |
- |
- case UseCase::TOUCHSTART: |
- new_policy.rail_mode = v8::PERFORMANCE_RESPONSE; |
- new_policy.compositor_queue_policy.priority = TaskQueue::HIGH_PRIORITY; |
- new_policy.loading_queue_policy.is_enabled = false; |
- new_policy.timer_queue_policy.is_enabled = false; |
- // NOTE this is a nop due to the above. |
- expensive_task_policy = ExpensiveTaskPolicy::BLOCK; |
- break; |
- |
- case UseCase::NONE: |
- // It's only safe to block tasks that if we are expecting a compositor |
- // driven gesture. |
- if (touchstart_expected_soon && |
- AnyThread().last_gesture_was_compositor_driven) { |
- new_policy.rail_mode = v8::PERFORMANCE_RESPONSE; |
- expensive_task_policy = ExpensiveTaskPolicy::BLOCK; |
- } |
- break; |
- |
- case UseCase::LOADING: |
- new_policy.rail_mode = v8::PERFORMANCE_LOAD; |
- new_policy.loading_queue_policy.priority = TaskQueue::HIGH_PRIORITY; |
- new_policy.default_queue_policy.priority = TaskQueue::HIGH_PRIORITY; |
- break; |
- |
- default: |
- NOTREACHED(); |
- } |
- |
- // TODO(skyostil): Add an idle state for foreground tabs too. |
- if (MainThreadOnly().renderer_hidden) |
- new_policy.rail_mode = v8::PERFORMANCE_IDLE; |
- |
- if (expensive_task_policy == ExpensiveTaskPolicy::BLOCK && |
- (!MainThreadOnly().expensive_task_blocking_allowed || |
- !MainThreadOnly().have_seen_a_begin_main_frame || |
- MainThreadOnly().navigation_task_expected_count > 0)) { |
- expensive_task_policy = ExpensiveTaskPolicy::RUN; |
- } |
- |
- switch (expensive_task_policy) { |
- case ExpensiveTaskPolicy::RUN: |
- break; |
- |
- case ExpensiveTaskPolicy::BLOCK: |
- if (loading_tasks_seem_expensive) |
- new_policy.loading_queue_policy.is_enabled = false; |
- if (timer_tasks_seem_expensive) |
- new_policy.timer_queue_policy.is_enabled = false; |
- break; |
- |
- case ExpensiveTaskPolicy::THROTTLE: |
- if (loading_tasks_seem_expensive) { |
- new_policy.loading_queue_policy.time_domain_type = |
- TimeDomainType::THROTTLED; |
- } |
- if (timer_tasks_seem_expensive) { |
- new_policy.timer_queue_policy.time_domain_type = |
- TimeDomainType::THROTTLED; |
- } |
- break; |
- } |
- MainThreadOnly().expensive_task_policy = expensive_task_policy; |
- |
- if (MainThreadOnly().timer_queue_suspend_count != 0 || |
- MainThreadOnly().timer_queue_suspended_when_backgrounded) { |
- new_policy.timer_queue_policy.is_enabled = false; |
- // TODO(alexclarke): Figure out if we really need to do this. |
- new_policy.timer_queue_policy.time_domain_type = TimeDomainType::REAL; |
- } |
- |
- if (MainThreadOnly().renderer_suspended) { |
- new_policy.loading_queue_policy.is_enabled = false; |
- DCHECK(!new_policy.timer_queue_policy.is_enabled); |
- } |
- |
- if (MainThreadOnly().use_virtual_time) { |
- new_policy.compositor_queue_policy.time_domain_type = |
- TimeDomainType::VIRTUAL; |
- new_policy.default_queue_policy.time_domain_type = TimeDomainType::VIRTUAL; |
- new_policy.loading_queue_policy.time_domain_type = TimeDomainType::VIRTUAL; |
- new_policy.timer_queue_policy.time_domain_type = TimeDomainType::VIRTUAL; |
- } |
- |
- // Tracing is done before the early out check, because it's quite possible we |
- // will otherwise miss this information in traces. |
- TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
- TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", |
- this, AsValueLocked(now)); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "use_case", |
- use_case); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "rail_mode", |
- new_policy.rail_mode); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "touchstart_expected_soon", |
- MainThreadOnly().touchstart_expected_soon); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "expensive_task_policy", expensive_task_policy); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererScheduler.loading_tasks_seem_expensive", |
- MainThreadOnly().loading_tasks_seem_expensive); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererScheduler.timer_tasks_seem_expensive", |
- MainThreadOnly().timer_tasks_seem_expensive); |
- |
- // TODO(alexclarke): Can we get rid of force update now? |
- if (update_type == UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED && |
- new_policy == MainThreadOnly().current_policy) { |
- return; |
- } |
- |
- ApplyTaskQueuePolicy(compositor_task_runner_.get(), |
- MainThreadOnly().current_policy.compositor_queue_policy, |
- new_policy.compositor_queue_policy); |
- |
- for (const scoped_refptr<TaskQueue>& loading_queue : loading_task_runners_) { |
- ApplyTaskQueuePolicy(loading_queue.get(), |
- MainThreadOnly().current_policy.loading_queue_policy, |
- new_policy.loading_queue_policy); |
- } |
- |
- for (const scoped_refptr<TaskQueue>& timer_queue : timer_task_runners_) { |
- ApplyTaskQueuePolicy(timer_queue.get(), |
- MainThreadOnly().current_policy.timer_queue_policy, |
- new_policy.timer_queue_policy); |
- } |
- MainThreadOnly().have_reported_blocking_intervention_in_current_policy = |
- false; |
- |
- // TODO(alexclarke): We shouldn't have to prioritize the default queue, but it |
- // appears to be necessary since the order of loading tasks and IPCs (which |
- // are mostly dispatched on the default queue) need to be preserved. |
- ApplyTaskQueuePolicy(helper_.DefaultTaskRunner().get(), |
- MainThreadOnly().current_policy.default_queue_policy, |
- new_policy.default_queue_policy); |
- if (MainThreadOnly().rail_mode_observer && |
- new_policy.rail_mode != MainThreadOnly().current_policy.rail_mode) { |
- MainThreadOnly().rail_mode_observer->OnRAILModeChanged( |
- new_policy.rail_mode); |
- } |
- |
- DCHECK(compositor_task_runner_->IsQueueEnabled()); |
- MainThreadOnly().current_policy = new_policy; |
-} |
- |
-void RendererSchedulerImpl::ApplyTaskQueuePolicy( |
- TaskQueue* task_queue, |
- const TaskQueuePolicy& old_task_queue_policy, |
- const TaskQueuePolicy& new_task_queue_policy) const { |
- if (old_task_queue_policy.is_enabled != new_task_queue_policy.is_enabled) { |
- throttling_helper_->SetQueueEnabled(task_queue, |
- new_task_queue_policy.is_enabled); |
- } |
- |
- if (old_task_queue_policy.priority != new_task_queue_policy.priority) |
- task_queue->SetQueuePriority(new_task_queue_policy.priority); |
- |
- if (old_task_queue_policy.time_domain_type != |
- new_task_queue_policy.time_domain_type) { |
- if (old_task_queue_policy.time_domain_type == TimeDomainType::THROTTLED) { |
- throttling_helper_->DecreaseThrottleRefCount(task_queue); |
- } else if (new_task_queue_policy.time_domain_type == |
- TimeDomainType::THROTTLED) { |
- throttling_helper_->IncreaseThrottleRefCount(task_queue); |
- } else if (new_task_queue_policy.time_domain_type == |
- TimeDomainType::VIRTUAL) { |
- DCHECK(virtual_time_domain_); |
- task_queue->SetTimeDomain(virtual_time_domain_.get()); |
- } |
- } |
-} |
- |
-RendererSchedulerImpl::UseCase RendererSchedulerImpl::ComputeCurrentUseCase( |
- base::TimeTicks now, |
- base::TimeDelta* expected_use_case_duration) const { |
- any_thread_lock_.AssertAcquired(); |
- // Special case for flings. This is needed because we don't get notification |
- // of a fling ending (although we do for cancellation). |
- if (AnyThread().fling_compositor_escalation_deadline > now && |
- !AnyThread().awaiting_touch_start_response) { |
- *expected_use_case_duration = |
- AnyThread().fling_compositor_escalation_deadline - now; |
- return UseCase::COMPOSITOR_GESTURE; |
- } |
- // Above all else we want to be responsive to user input. |
- *expected_use_case_duration = |
- AnyThread().user_model.TimeLeftInUserGesture(now); |
- if (*expected_use_case_duration > base::TimeDelta()) { |
- // Has a gesture been fully established? |
- if (AnyThread().awaiting_touch_start_response) { |
- // No, so arrange for compositor tasks to be run at the highest priority. |
- return UseCase::TOUCHSTART; |
- } |
- |
- // Yes a gesture has been established. Based on how the gesture is handled |
- // we need to choose between one of four use cases: |
- // 1. COMPOSITOR_GESTURE where the gesture is processed only on the |
- // compositor thread. |
- // 2. MAIN_THREAD_GESTURE where the gesture is processed only on the main |
- // thread. |
- // 3. MAIN_THREAD_CUSTOM_INPUT_HANDLING where the main thread processes a |
- // stream of input events and has prevented a default gesture from being |
- // started. |
- // 4. SYNCHRONIZED_GESTURE where the gesture is processed on both threads. |
- // TODO(skyostil): Consider removing in_idle_period_ and |
- // HadAnIdlePeriodRecently() unless we need them here. |
- if (AnyThread().last_gesture_was_compositor_driven) { |
- if (AnyThread().begin_main_frame_on_critical_path) { |
- return UseCase::SYNCHRONIZED_GESTURE; |
- } else { |
- return UseCase::COMPOSITOR_GESTURE; |
- } |
- } |
- if (AnyThread().default_gesture_prevented) { |
- return UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING; |
- } else { |
- return UseCase::MAIN_THREAD_GESTURE; |
- } |
- } |
- |
- // TODO(alexclarke): return UseCase::LOADING if signals suggest the system is |
- // in the initial 1s of RAIL loading. |
- return UseCase::NONE; |
-} |
- |
-base::TimeDelta RendererSchedulerImpl::EstimateLongestJankFreeTaskDuration() |
- const { |
- switch (MainThreadOnly().current_use_case) { |
- case UseCase::TOUCHSTART: |
- case UseCase::COMPOSITOR_GESTURE: |
- case UseCase::LOADING: |
- case UseCase::NONE: |
- return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis); |
- |
- case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING: |
- case UseCase::MAIN_THREAD_GESTURE: |
- case UseCase::SYNCHRONIZED_GESTURE: |
- return MainThreadOnly().idle_time_estimator.GetExpectedIdleDuration( |
- MainThreadOnly().compositor_frame_interval); |
- |
- default: |
- NOTREACHED(); |
- return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis); |
- } |
-} |
- |
-bool RendererSchedulerImpl::CanEnterLongIdlePeriod( |
- base::TimeTicks now, |
- base::TimeDelta* next_long_idle_period_delay_out) { |
- helper_.CheckOnValidThread(); |
- |
- MaybeUpdatePolicy(); |
- if (MainThreadOnly().current_use_case == UseCase::TOUCHSTART) { |
- // Don't start a long idle task in touch start priority, try again when |
- // the policy is scheduled to end. |
- *next_long_idle_period_delay_out = |
- std::max(base::TimeDelta(), |
- MainThreadOnly().current_policy_expiration_time - now); |
- return false; |
- } |
- return true; |
-} |
- |
-SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() { |
- return &helper_; |
-} |
- |
-TaskCostEstimator* |
-RendererSchedulerImpl::GetLoadingTaskCostEstimatorForTesting() { |
- return &MainThreadOnly().loading_task_cost_estimator; |
-} |
- |
-TaskCostEstimator* |
-RendererSchedulerImpl::GetTimerTaskCostEstimatorForTesting() { |
- return &MainThreadOnly().timer_task_cost_estimator; |
-} |
- |
-IdleTimeEstimator* RendererSchedulerImpl::GetIdleTimeEstimatorForTesting() { |
- return &MainThreadOnly().idle_time_estimator; |
-} |
- |
-void RendererSchedulerImpl::SuspendTimerQueue() { |
- MainThreadOnly().timer_queue_suspend_count++; |
- ForceUpdatePolicy(); |
-#ifndef NDEBUG |
- DCHECK(!default_timer_task_runner_->IsQueueEnabled()); |
- for (const auto& runner : timer_task_runners_) { |
- DCHECK(!runner->IsQueueEnabled()); |
- } |
-#endif |
-} |
- |
-void RendererSchedulerImpl::ResumeTimerQueue() { |
- MainThreadOnly().timer_queue_suspend_count--; |
- DCHECK_GE(MainThreadOnly().timer_queue_suspend_count, 0); |
- ForceUpdatePolicy(); |
-} |
- |
-void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled( |
- bool enabled) { |
- // Note that this will only take effect for the next backgrounded signal. |
- MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled = enabled; |
-} |
- |
-std::unique_ptr<base::trace_event::ConvertableToTraceFormat> |
-RendererSchedulerImpl::AsValue(base::TimeTicks optional_now) const { |
- base::AutoLock lock(any_thread_lock_); |
- return AsValueLocked(optional_now); |
-} |
- |
-// static |
-const char* RendererSchedulerImpl::ExpensiveTaskPolicyToString( |
- ExpensiveTaskPolicy expensive_task_policy) { |
- switch (expensive_task_policy) { |
- case ExpensiveTaskPolicy::RUN: |
- return "RUN"; |
- case ExpensiveTaskPolicy::BLOCK: |
- return "BLOCK"; |
- case ExpensiveTaskPolicy::THROTTLE: |
- return "THROTTLE"; |
- default: |
- NOTREACHED(); |
- return nullptr; |
- } |
-} |
- |
-std::unique_ptr<base::trace_event::ConvertableToTraceFormat> |
-RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { |
- helper_.CheckOnValidThread(); |
- any_thread_lock_.AssertAcquired(); |
- |
- if (optional_now.is_null()) |
- optional_now = helper_.scheduler_tqm_delegate()->NowTicks(); |
- std::unique_ptr<base::trace_event::TracedValue> state( |
- new base::trace_event::TracedValue()); |
- state->SetBoolean( |
- "has_visible_render_widget_with_touch_handler", |
- MainThreadOnly().has_visible_render_widget_with_touch_handler); |
- state->SetString("current_use_case", |
- UseCaseToString(MainThreadOnly().current_use_case)); |
- state->SetString("rail_mode", |
- RAILModeToString(MainThreadOnly().current_policy.rail_mode)); |
- state->SetBoolean("expensive_task_blocking_allowed", |
- MainThreadOnly().expensive_task_blocking_allowed); |
- state->SetBoolean("loading_tasks_seem_expensive", |
- MainThreadOnly().loading_tasks_seem_expensive); |
- state->SetBoolean("timer_tasks_seem_expensive", |
- MainThreadOnly().timer_tasks_seem_expensive); |
- state->SetBoolean("begin_frame_not_expected_soon", |
- MainThreadOnly().begin_frame_not_expected_soon); |
- state->SetBoolean("touchstart_expected_soon", |
- MainThreadOnly().touchstart_expected_soon); |
- state->SetString("idle_period_state", |
- IdleHelper::IdlePeriodStateToString( |
- idle_helper_.SchedulerIdlePeriodState())); |
- state->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden); |
- state->SetBoolean("have_seen_a_begin_main_frame", |
- MainThreadOnly().have_seen_a_begin_main_frame); |
- state->SetBoolean( |
- "have_reported_blocking_intervention_in_current_policy", |
- MainThreadOnly().have_reported_blocking_intervention_in_current_policy); |
- state->SetBoolean( |
- "have_reported_blocking_intervention_since_navigation", |
- MainThreadOnly().have_reported_blocking_intervention_since_navigation); |
- state->SetBoolean("renderer_backgrounded", |
- MainThreadOnly().renderer_backgrounded); |
- state->SetBoolean("timer_queue_suspended_when_backgrounded", |
- MainThreadOnly().timer_queue_suspended_when_backgrounded); |
- state->SetInteger("timer_queue_suspend_count", |
- MainThreadOnly().timer_queue_suspend_count); |
- state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF()); |
- state->SetDouble( |
- "rails_loading_priority_deadline", |
- (AnyThread().rails_loading_priority_deadline - base::TimeTicks()) |
- .InMillisecondsF()); |
- state->SetDouble( |
- "fling_compositor_escalation_deadline", |
- (AnyThread().fling_compositor_escalation_deadline - base::TimeTicks()) |
- .InMillisecondsF()); |
- state->SetInteger("navigation_task_expected_count", |
- MainThreadOnly().navigation_task_expected_count); |
- state->SetDouble("last_idle_period_end_time", |
- (AnyThread().last_idle_period_end_time - base::TimeTicks()) |
- .InMillisecondsF()); |
- state->SetBoolean("awaiting_touch_start_response", |
- AnyThread().awaiting_touch_start_response); |
- state->SetBoolean("begin_main_frame_on_critical_path", |
- AnyThread().begin_main_frame_on_critical_path); |
- state->SetBoolean("last_gesture_was_compositor_driven", |
- AnyThread().last_gesture_was_compositor_driven); |
- state->SetBoolean("default_gesture_prevented", |
- AnyThread().default_gesture_prevented); |
- state->SetDouble("expected_loading_task_duration", |
- MainThreadOnly() |
- .loading_task_cost_estimator.expected_task_duration() |
- .InMillisecondsF()); |
- state->SetDouble("expected_timer_task_duration", |
- MainThreadOnly() |
- .timer_task_cost_estimator.expected_task_duration() |
- .InMillisecondsF()); |
- // TODO(skyostil): Can we somehow trace how accurate these estimates were? |
- state->SetDouble( |
- "longest_jank_free_task_duration", |
- MainThreadOnly().longest_jank_free_task_duration.InMillisecondsF()); |
- state->SetDouble( |
- "compositor_frame_interval", |
- MainThreadOnly().compositor_frame_interval.InMillisecondsF()); |
- state->SetDouble( |
- "estimated_next_frame_begin", |
- (MainThreadOnly().estimated_next_frame_begin - base::TimeTicks()) |
- .InMillisecondsF()); |
- state->SetBoolean("in_idle_period", AnyThread().in_idle_period); |
- |
- state->SetString( |
- "expensive_task_policy", |
- ExpensiveTaskPolicyToString(MainThreadOnly().expensive_task_policy)); |
- |
- AnyThread().user_model.AsValueInto(state.get()); |
- render_widget_scheduler_signals_.AsValueInto(state.get()); |
- |
- return std::move(state); |
-} |
- |
-void RendererSchedulerImpl::OnIdlePeriodStarted() { |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().in_idle_period = true; |
- UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
-} |
- |
-void RendererSchedulerImpl::OnIdlePeriodEnded() { |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().last_idle_period_end_time = |
- helper_.scheduler_tqm_delegate()->NowTicks(); |
- AnyThread().in_idle_period = false; |
- UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
-} |
- |
-void RendererSchedulerImpl::AddPendingNavigation( |
- blink::WebScheduler::NavigatingFrameType type) { |
- helper_.CheckOnValidThread(); |
- if (type == blink::WebScheduler::NavigatingFrameType::kMainFrame) { |
- MainThreadOnly().navigation_task_expected_count++; |
- UpdatePolicy(); |
- } |
-} |
- |
-void RendererSchedulerImpl::RemovePendingNavigation( |
- blink::WebScheduler::NavigatingFrameType type) { |
- helper_.CheckOnValidThread(); |
- DCHECK_GT(MainThreadOnly().navigation_task_expected_count, 0); |
- if (type == blink::WebScheduler::NavigatingFrameType::kMainFrame && |
- MainThreadOnly().navigation_task_expected_count > 0) { |
- MainThreadOnly().navigation_task_expected_count--; |
- UpdatePolicy(); |
- } |
-} |
- |
-void RendererSchedulerImpl::OnNavigationStarted() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "RendererSchedulerImpl::OnNavigationStarted"); |
- base::AutoLock lock(any_thread_lock_); |
- AnyThread().rails_loading_priority_deadline = |
- helper_.scheduler_tqm_delegate()->NowTicks() + |
- base::TimeDelta::FromMilliseconds( |
- kRailsInitialLoadingPrioritizationMillis); |
- ResetForNavigationLocked(); |
-} |
- |
-bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now) const { |
- return (now - AnyThread().last_idle_period_end_time) <= |
- base::TimeDelta::FromMilliseconds( |
- kIdlePeriodStarvationThresholdMillis); |
-} |
- |
-void RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded() { |
- DCHECK(MainThreadOnly().renderer_backgrounded); |
- if (MainThreadOnly().timer_queue_suspended_when_backgrounded) |
- return; |
- |
- MainThreadOnly().timer_queue_suspended_when_backgrounded = true; |
- ForceUpdatePolicy(); |
-} |
- |
-void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() { |
- DCHECK(!MainThreadOnly().renderer_backgrounded); |
- if (!MainThreadOnly().timer_queue_suspended_when_backgrounded) |
- return; |
- |
- MainThreadOnly().timer_queue_suspended_when_backgrounded = false; |
- ForceUpdatePolicy(); |
-} |
- |
-void RendererSchedulerImpl::ResetForNavigationLocked() { |
- helper_.CheckOnValidThread(); |
- any_thread_lock_.AssertAcquired(); |
- AnyThread().user_model.Reset(helper_.scheduler_tqm_delegate()->NowTicks()); |
- AnyThread().have_seen_touchstart = false; |
- MainThreadOnly().loading_task_cost_estimator.Clear(); |
- MainThreadOnly().timer_task_cost_estimator.Clear(); |
- MainThreadOnly().idle_time_estimator.Clear(); |
- MainThreadOnly().have_seen_a_begin_main_frame = false; |
- MainThreadOnly().have_reported_blocking_intervention_since_navigation = false; |
- UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
-} |
- |
-void RendererSchedulerImpl::SetTopLevelBlameContext( |
- base::trace_event::BlameContext* blame_context) { |
- // Any task that runs in the default task runners belongs to the context of |
- // all frames (as opposed to a particular frame). Note that the task itself |
- // may still enter a more specific blame context if necessary. |
- // |
- // Per-frame task runners (loading, timers, etc.) are configured with a more |
- // specific blame context by WebFrameSchedulerImpl. |
- control_task_runner_->SetBlameContext(blame_context); |
- DefaultTaskRunner()->SetBlameContext(blame_context); |
- default_loading_task_runner_->SetBlameContext(blame_context); |
- default_timer_task_runner_->SetBlameContext(blame_context); |
- compositor_task_runner_->SetBlameContext(blame_context); |
- idle_helper_.IdleTaskRunner()->SetBlameContext(blame_context); |
-} |
- |
-void RendererSchedulerImpl::SetRAILModeObserver(RAILModeObserver* observer) { |
- MainThreadOnly().rail_mode_observer = observer; |
-} |
- |
-void RendererSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) { |
- helper_.RegisterTimeDomain(time_domain); |
-} |
- |
-void RendererSchedulerImpl::UnregisterTimeDomain(TimeDomain* time_domain) { |
- helper_.UnregisterTimeDomain(time_domain); |
-} |
- |
-void RendererSchedulerImpl::SetExpensiveTaskBlockingAllowed(bool allowed) { |
- MainThreadOnly().expensive_task_blocking_allowed = allowed; |
-} |
- |
-base::TickClock* RendererSchedulerImpl::tick_clock() const { |
- return helper_.scheduler_tqm_delegate().get(); |
-} |
- |
-void RendererSchedulerImpl::AddWebViewScheduler( |
- WebViewSchedulerImpl* web_view_scheduler) { |
- MainThreadOnly().web_view_schedulers.insert(web_view_scheduler); |
-} |
- |
-void RendererSchedulerImpl::RemoveWebViewScheduler( |
- WebViewSchedulerImpl* web_view_scheduler) { |
- DCHECK(MainThreadOnly().web_view_schedulers.find(web_view_scheduler) != |
- MainThreadOnly().web_view_schedulers.end()); |
- MainThreadOnly().web_view_schedulers.erase(web_view_scheduler); |
-} |
- |
-void RendererSchedulerImpl::BroadcastConsoleWarning( |
- const std::string& message) { |
- helper_.CheckOnValidThread(); |
- for (auto* web_view_scheduler : MainThreadOnly().web_view_schedulers) |
- web_view_scheduler->AddConsoleWarning(message); |
-} |
- |
-void RendererSchedulerImpl::OnTriedToExecuteBlockedTask( |
- const TaskQueue& queue, |
- const base::PendingTask& task) { |
- if (!MainThreadOnly().expensive_task_blocking_allowed || |
- MainThreadOnly().current_use_case == UseCase::TOUCHSTART || |
- MainThreadOnly().longest_jank_free_task_duration < |
- base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis) || |
- MainThreadOnly().timer_queue_suspend_count || |
- MainThreadOnly().timer_queue_suspended_when_backgrounded) { |
- return; |
- } |
- if (!MainThreadOnly().timer_tasks_seem_expensive && |
- !MainThreadOnly().loading_tasks_seem_expensive) { |
- return; |
- } |
- if (!MainThreadOnly().have_reported_blocking_intervention_in_current_policy) { |
- MainThreadOnly().have_reported_blocking_intervention_in_current_policy = |
- true; |
- TRACE_EVENT_INSTANT0("renderer.scheduler", |
- "RendererSchedulerImpl::TaskBlocked", |
- TRACE_EVENT_SCOPE_THREAD); |
- } |
- |
- if (!MainThreadOnly().have_reported_blocking_intervention_since_navigation) { |
- { |
- base::AutoLock lock(any_thread_lock_); |
- if (!AnyThread().have_seen_touchstart) |
- return; |
- } |
- MainThreadOnly().have_reported_blocking_intervention_since_navigation = |
- true; |
- BroadcastConsoleWarning( |
- "Blink deferred a task in order to make scrolling smoother. " |
- "Your timer and network tasks should take less than 50ms to run " |
- "to avoid this. Please see " |
- "https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail" |
- " and https://crbug.com/574343#c40 for more information."); |
- } |
-} |
- |
-void RendererSchedulerImpl::ReportTaskTime(base::TimeTicks start_time, |
- base::TimeTicks end_time) { |
- MainThreadOnly().queueing_time_estimator.OnToplevelTaskCompleted(start_time, |
- end_time); |
- MainThreadOnly().long_task_tracker.RecordLongTask( |
- start_time, end_time - start_time); |
- UMA_HISTOGRAM_CUSTOM_COUNTS("RendererScheduler.TaskTime", |
- (end_time - start_time).InMicroseconds(), 1, |
- 1000000, 50); |
-} |
- |
-LongTaskTracker::LongTaskTiming RendererSchedulerImpl::GetLongTaskTiming() { |
- return MainThreadOnly().long_task_tracker.GetLongTaskTiming(); |
-} |
- |
-void RendererSchedulerImpl::OnQueueingTimeForWindowEstimated( |
- base::TimeDelta queueing_time) { |
- UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration", |
- queueing_time); |
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
- "estimated_queueing_time_for_window", |
- queueing_time.InMillisecondsF()); |
-} |
- |
-AutoAdvancingVirtualTimeDomain* RendererSchedulerImpl::GetVirtualTimeDomain() { |
- if (!virtual_time_domain_) { |
- virtual_time_domain_.reset( |
- new AutoAdvancingVirtualTimeDomain(tick_clock()->NowTicks())); |
- RegisterTimeDomain(virtual_time_domain_.get()); |
- } |
- return virtual_time_domain_.get(); |
-} |
- |
-void RendererSchedulerImpl::EnableVirtualTime() { |
- MainThreadOnly().use_virtual_time = true; |
- |
- // The |unthrottled_task_runners_| are not actively managed by UpdatePolicy(). |
- AutoAdvancingVirtualTimeDomain* time_domain = GetVirtualTimeDomain(); |
- for (const scoped_refptr<TaskQueue>& task_queue : unthrottled_task_runners_) |
- task_queue->SetTimeDomain(time_domain); |
- |
- throttling_helper_->EnableVirtualTime(); |
- |
- ForceUpdatePolicy(); |
-} |
- |
-// static |
-const char* RendererSchedulerImpl::UseCaseToString(UseCase use_case) { |
- switch (use_case) { |
- case UseCase::NONE: |
- return "none"; |
- case UseCase::COMPOSITOR_GESTURE: |
- return "compositor_gesture"; |
- case UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING: |
- return "main_thread_custom_input_handling"; |
- case UseCase::SYNCHRONIZED_GESTURE: |
- return "synchronized_gesture"; |
- case UseCase::TOUCHSTART: |
- return "touchstart"; |
- case UseCase::LOADING: |
- return "loading"; |
- case UseCase::MAIN_THREAD_GESTURE: |
- return "main_thread_gesture"; |
- default: |
- NOTREACHED(); |
- return nullptr; |
- } |
-} |
- |
-// static |
-const char* RendererSchedulerImpl::RAILModeToString(v8::RAILMode rail_mode) { |
- switch (rail_mode) { |
- case v8::PERFORMANCE_RESPONSE: |
- return "response"; |
- case v8::PERFORMANCE_ANIMATION: |
- return "animation"; |
- case v8::PERFORMANCE_IDLE: |
- return "idle"; |
- case v8::PERFORMANCE_LOAD: |
- return "load"; |
- default: |
- NOTREACHED(); |
- return nullptr; |
- } |
-} |
- |
-} // namespace scheduler |