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

Unified Diff: components/scheduler/renderer/renderer_scheduler_impl.cc

Issue 2118903002: scheduler: Move the Blink scheduler into Blink (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698