| // 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. |
| |
| #ifndef COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_ |
| #define COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_ |
| |
| #include "base/atomicops.h" |
| #include "base/macros.h" |
| #include "base/synchronization/lock.h" |
| #include "components/scheduler/base/pollable_thread_safe_flag.h" |
| #include "components/scheduler/child/idle_helper.h" |
| #include "components/scheduler/child/scheduler_helper.h" |
| #include "components/scheduler/renderer/deadline_task_runner.h" |
| #include "components/scheduler/renderer/idle_time_estimator.h" |
| #include "components/scheduler/renderer/render_widget_signals.h" |
| #include "components/scheduler/renderer/renderer_scheduler.h" |
| #include "components/scheduler/renderer/task_cost_estimator.h" |
| #include "components/scheduler/renderer/throttling_helper.h" |
| #include "components/scheduler/renderer/user_model.h" |
| #include "components/scheduler/renderer/web_view_scheduler_impl.h" |
| #include "components/scheduler/scheduler_export.h" |
| |
| namespace base { |
| namespace trace_event { |
| class ConvertableToTraceFormat; |
| } |
| } |
| |
| namespace scheduler { |
| class RenderWidgetSchedulingState; |
| class WebViewSchedulerImpl; |
| class ThrottlingHelper; |
| |
| class SCHEDULER_EXPORT RendererSchedulerImpl |
| : public RendererScheduler, |
| public IdleHelper::Delegate, |
| public SchedulerHelper::Observer, |
| public RenderWidgetSignals::Observer { |
| public: |
| RendererSchedulerImpl(scoped_refptr<SchedulerTqmDelegate> main_task_runner); |
| ~RendererSchedulerImpl() override; |
| |
| // RendererScheduler implementation: |
| scoped_ptr<blink::WebThread> CreateMainThread() override; |
| scoped_refptr<TaskQueue> DefaultTaskRunner() override; |
| scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override; |
| scoped_refptr<TaskQueue> CompositorTaskRunner() override; |
| scoped_refptr<TaskQueue> LoadingTaskRunner() override; |
| scoped_refptr<TaskQueue> TimerTaskRunner() override; |
| scoped_refptr<TaskQueue> NewLoadingTaskRunner(const char* name) override; |
| scoped_refptr<TaskQueue> NewTimerTaskRunner(const char* name) override; |
| scoped_ptr<RenderWidgetSchedulingState> NewRenderWidgetSchedulingState() |
| override; |
| void WillBeginFrame(const cc::BeginFrameArgs& args) override; |
| void BeginFrameNotExpectedSoon() override; |
| void DidCommitFrameToCompositor() override; |
| void DidHandleInputEventOnCompositorThread( |
| const blink::WebInputEvent& web_input_event, |
| InputEventState event_state) override; |
| void DidHandleInputEventOnMainThread( |
| const blink::WebInputEvent& web_input_event) override; |
| void DidAnimateForInputOnCompositorThread() override; |
| void OnRendererBackgrounded() override; |
| void OnRendererForegrounded() override; |
| void AddPendingNavigation() override; |
| void RemovePendingNavigation() override; |
| void OnNavigationStarted() override; |
| bool IsHighPriorityWorkAnticipated() override; |
| bool ShouldYieldForHighPriorityWork() override; |
| bool CanExceedIdleDeadlineIfRequired() const override; |
| void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override; |
| void RemoveTaskObserver( |
| base::MessageLoop::TaskObserver* task_observer) override; |
| void Shutdown() override; |
| void SuspendTimerQueue() override; |
| void ResumeTimerQueue() override; |
| void SetTimerQueueSuspensionWhenBackgroundedEnabled(bool enabled) override; |
| |
| // RenderWidgetSignals::Observer implementation: |
| void SetAllRenderWidgetsHidden(bool hidden) override; |
| void SetHasVisibleRenderWidgetWithTouchHandler( |
| bool has_visible_render_widget_with_touch_handler) override; |
| |
| // SchedulerHelper::Observer implementation: |
| void OnUnregisterTaskQueue(const scoped_refptr<TaskQueue>& queue) override; |
| void OnTriedToExecuteBlockedTask(const TaskQueue& queue, |
| const base::PendingTask& task) override; |
| |
| // Returns a task runner where tasks run at the highest possible priority. |
| scoped_refptr<TaskQueue> ControlTaskRunner(); |
| |
| void RegisterTimeDomain(TimeDomain* time_domain); |
| void UnregisterTimeDomain(TimeDomain* time_domain); |
| |
| void SetExpensiveTaskBlockingAllowed(bool allowed); |
| |
| void AddWebViewScheduler(WebViewSchedulerImpl* web_view_scheduler); |
| void RemoveWebViewScheduler(WebViewSchedulerImpl* web_view_scheduler); |
| |
| // Test helpers. |
| SchedulerHelper* GetSchedulerHelperForTesting(); |
| TaskCostEstimator* GetLoadingTaskCostEstimatorForTesting(); |
| TaskCostEstimator* GetTimerTaskCostEstimatorForTesting(); |
| IdleTimeEstimator* GetIdleTimeEstimatorForTesting(); |
| base::TimeTicks CurrentIdleTaskDeadlineForTesting() const; |
| |
| base::TickClock* tick_clock() const; |
| |
| RealTimeDomain* real_time_domain() const { |
| return helper_.real_time_domain(); |
| } |
| |
| ThrottlingHelper* throttling_helper() { return throttling_helper_.get(); } |
| |
| private: |
| friend class RendererSchedulerImplTest; |
| friend class RendererSchedulerImplForTest; |
| friend class RenderWidgetSchedulingState; |
| |
| enum class TimeDomainType { |
| REAL, |
| THROTTLED, |
| }; |
| |
| struct TaskQueuePolicy { |
| TaskQueuePolicy() |
| : is_enabled(true), |
| priority(TaskQueue::NORMAL_PRIORITY), |
| time_domain_type(TimeDomainType::REAL) {} |
| |
| bool is_enabled; |
| TaskQueue::QueuePriority priority; |
| TimeDomainType time_domain_type; |
| |
| bool operator==(const TaskQueuePolicy& other) const { |
| return is_enabled == other.is_enabled && priority == other.priority && |
| time_domain_type == other.time_domain_type; |
| } |
| }; |
| |
| struct Policy { |
| TaskQueuePolicy compositor_queue_policy; |
| TaskQueuePolicy loading_queue_policy; |
| TaskQueuePolicy timer_queue_policy; |
| TaskQueuePolicy default_queue_policy; |
| |
| bool operator==(const Policy& other) const { |
| return compositor_queue_policy == other.compositor_queue_policy && |
| loading_queue_policy == other.loading_queue_policy && |
| timer_queue_policy == other.timer_queue_policy && |
| default_queue_policy == other.default_queue_policy; |
| } |
| }; |
| |
| class PollableNeedsUpdateFlag { |
| public: |
| PollableNeedsUpdateFlag(base::Lock* write_lock); |
| ~PollableNeedsUpdateFlag(); |
| |
| // Set the flag. May only be called if |write_lock| is held. |
| void SetWhileLocked(bool value); |
| |
| // Returns true iff the flag is set to true. |
| bool IsSet() const; |
| |
| private: |
| base::subtle::Atomic32 flag_; |
| base::Lock* write_lock_; // Not owned. |
| |
| DISALLOW_COPY_AND_ASSIGN(PollableNeedsUpdateFlag); |
| }; |
| |
| // IdleHelper::Delegate implementation: |
| bool CanEnterLongIdlePeriod( |
| base::TimeTicks now, |
| base::TimeDelta* next_long_idle_period_delay_out) override; |
| void IsNotQuiescent() override {} |
| void OnIdlePeriodStarted() override; |
| void OnIdlePeriodEnded() override; |
| |
| void EndIdlePeriod(); |
| |
| // Returns the serialized scheduler state for tracing. |
| scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue( |
| base::TimeTicks optional_now) const; |
| scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValueLocked( |
| base::TimeTicks optional_now) const; |
| |
| static bool ShouldPrioritizeInputEvent( |
| const blink::WebInputEvent& web_input_event); |
| |
| // The amount of time which idle periods can continue being scheduled when the |
| // renderer has been hidden, before going to sleep for good. |
| static const int kEndIdleWhenHiddenDelayMillis = 10000; |
| |
| // The amount of time for which loading tasks will be prioritized over |
| // other tasks during the initial page load. |
| static const int kRailsInitialLoadingPrioritizationMillis = 1000; |
| |
| // The amount of time in milliseconds we have to respond to user input as |
| // defined by RAILS. |
| static const int kRailsResponseTimeMillis = 50; |
| |
| // For the purposes of deciding whether or not it's safe to turn timers and |
| // loading tasks on only in idle periods, we regard the system as being as |
| // being "idle period" starved if there hasn't been an idle period in the last |
| // 10 seconds. This was chosen to be long enough to cover most anticipated |
| // user gestures. |
| static const int kIdlePeriodStarvationThresholdMillis = 10000; |
| |
| // The amount of time to wait before suspending shared timers after the |
| // renderer has been backgrounded. This is used only if background suspension |
| // of shared timers is enabled. |
| static const int kSuspendTimersWhenBackgroundedDelayMillis = 5 * 60 * 1000; |
| |
| // The time we should stay in a priority-escalated mode after a call to |
| // DidAnimateForInputOnCompositorThread(). |
| static const int kFlingEscalationLimitMillis = 100; |
| |
| // Schedules an immediate PolicyUpdate, if there isn't one already pending and |
| // sets |policy_may_need_update_|. Note |any_thread_lock_| must be |
| // locked. |
| void EnsureUrgentPolicyUpdatePostedOnMainThread( |
| const tracked_objects::Location& from_here); |
| |
| // Update the policy if a new signal has arrived. Must be called from the main |
| // thread. |
| void MaybeUpdatePolicy(); |
| |
| // Locks |any_thread_lock_| and updates the scheduler policy. May early |
| // out if the policy is unchanged. Must be called from the main thread. |
| void UpdatePolicy(); |
| |
| // Like UpdatePolicy, except it doesn't early out. |
| void ForceUpdatePolicy(); |
| |
| enum class UpdateType { |
| MAY_EARLY_OUT_IF_POLICY_UNCHANGED, |
| FORCE_UPDATE, |
| }; |
| |
| // The implelemtation of UpdatePolicy & ForceUpdatePolicy. It is allowed to |
| // early out if |update_type| is MAY_EARLY_OUT_IF_POLICY_UNCHANGED. |
| virtual void UpdatePolicyLocked(UpdateType update_type); |
| |
| // Helper for computing the use case. |expected_usecase_duration| will be |
| // filled with the amount of time after which the use case should be updated |
| // again. If the duration is zero, a new use case update should not be |
| // scheduled. Must be called with |any_thread_lock_| held. Can be called from |
| // any thread. |
| UseCase ComputeCurrentUseCase( |
| base::TimeTicks now, |
| base::TimeDelta* expected_use_case_duration) const; |
| |
| // Works out if a gesture appears to be in progress based on the current |
| // input signals. Can be called from any thread. |
| bool InputSignalsSuggestGestureInProgress(base::TimeTicks now) const; |
| |
| // An input event of some sort happened, the policy may need updating. |
| void UpdateForInputEventOnCompositorThread(blink::WebInputEvent::Type type, |
| InputEventState input_event_state); |
| |
| // Returns true if there has been at least one idle period in the last |
| // |kIdlePeriodStarvationThresholdMillis|. |
| bool HadAnIdlePeriodRecently(base::TimeTicks now) const; |
| |
| // Helpers for safely suspending/resuming the timer queue after a |
| // background/foreground signal. |
| void SuspendTimerQueueWhenBackgrounded(); |
| void ResumeTimerQueueWhenForegrounded(); |
| |
| // The task cost estimators and the UserModel need to be reset upon page |
| // nagigation. This function does that. Must be called from the main thread. |
| void ResetForNavigationLocked(); |
| |
| // Estimates the maximum task length that won't cause a jank based on the |
| // current system state. Must be called from the main thread. |
| base::TimeDelta EstimateLongestJankFreeTaskDuration() const; |
| |
| // Log a console warning message to all WebViews in this process. |
| void BroadcastConsoleWarning(const std::string& message); |
| |
| void ApplyTaskQueuePolicy(TaskQueue* task_queue, |
| const TaskQueuePolicy& old_task_queue_policy, |
| const TaskQueuePolicy& new_task_queue_policy) const; |
| |
| SchedulerHelper helper_; |
| IdleHelper idle_helper_; |
| scoped_ptr<ThrottlingHelper> throttling_helper_; |
| RenderWidgetSignals render_widget_scheduler_signals_; |
| |
| const scoped_refptr<TaskQueue> control_task_runner_; |
| const scoped_refptr<TaskQueue> compositor_task_runner_; |
| std::set<scoped_refptr<TaskQueue>> loading_task_runners_; |
| std::set<scoped_refptr<TaskQueue>> timer_task_runners_; |
| scoped_refptr<TaskQueue> default_loading_task_runner_; |
| scoped_refptr<TaskQueue> default_timer_task_runner_; |
| |
| base::Closure update_policy_closure_; |
| DeadlineTaskRunner delayed_update_policy_runner_; |
| CancelableClosureHolder end_renderer_hidden_idle_period_closure_; |
| CancelableClosureHolder suspend_timers_when_backgrounded_closure_; |
| |
| // We have decided to improve thread safety at the cost of some boilerplate |
| // (the accessors) for the following data members. |
| |
| struct MainThreadOnly { |
| MainThreadOnly(const scoped_refptr<TaskQueue>& compositor_task_runner, |
| base::TickClock* time_source); |
| ~MainThreadOnly(); |
| |
| TaskCostEstimator loading_task_cost_estimator; |
| TaskCostEstimator timer_task_cost_estimator; |
| IdleTimeEstimator idle_time_estimator; |
| UseCase current_use_case; |
| Policy current_policy; |
| base::TimeTicks current_policy_expiration_time; |
| base::TimeTicks estimated_next_frame_begin; |
| base::TimeDelta compositor_frame_interval; |
| base::TimeDelta longest_jank_free_task_duration; |
| int timer_queue_suspend_count; // TIMER_TASK_QUEUE suspended if non-zero. |
| int navigation_task_expected_count; |
| bool renderer_hidden; |
| bool renderer_backgrounded; |
| bool timer_queue_suspension_when_backgrounded_enabled; |
| bool timer_queue_suspended_when_backgrounded; |
| bool was_shutdown; |
| bool loading_tasks_seem_expensive; |
| bool timer_tasks_seem_expensive; |
| bool touchstart_expected_soon; |
| bool have_seen_a_begin_main_frame; |
| bool have_reported_blocking_intervention_in_current_policy; |
| bool have_reported_blocking_intervention_since_navigation; |
| bool has_visible_render_widget_with_touch_handler; |
| bool begin_frame_not_expected_soon; |
| bool expensive_task_blocking_allowed; |
| std::set<WebViewSchedulerImpl*> web_view_schedulers_; // Not owned. |
| }; |
| |
| struct AnyThread { |
| AnyThread(); |
| ~AnyThread(); |
| |
| base::TimeTicks last_idle_period_end_time; |
| base::TimeTicks rails_loading_priority_deadline; |
| base::TimeTicks fling_compositor_escalation_deadline; |
| UserModel user_model; |
| bool awaiting_touch_start_response; |
| bool in_idle_period; |
| bool begin_main_frame_on_critical_path; |
| bool last_gesture_was_compositor_driven; |
| bool have_seen_touchstart; |
| }; |
| |
| struct CompositorThreadOnly { |
| CompositorThreadOnly(); |
| ~CompositorThreadOnly(); |
| |
| blink::WebInputEvent::Type last_input_type; |
| scoped_ptr<base::ThreadChecker> compositor_thread_checker; |
| |
| void CheckOnValidThread() { |
| #if DCHECK_IS_ON() |
| // We don't actually care which thread this called from, just so long as |
| // its consistent. |
| if (!compositor_thread_checker) |
| compositor_thread_checker.reset(new base::ThreadChecker()); |
| DCHECK(compositor_thread_checker->CalledOnValidThread()); |
| #endif |
| } |
| }; |
| |
| // Don't access main_thread_only_, instead use MainThreadOnly(). |
| MainThreadOnly main_thread_only_; |
| MainThreadOnly& MainThreadOnly() { |
| helper_.CheckOnValidThread(); |
| return main_thread_only_; |
| } |
| const struct MainThreadOnly& MainThreadOnly() const { |
| helper_.CheckOnValidThread(); |
| return main_thread_only_; |
| } |
| |
| mutable base::Lock any_thread_lock_; |
| // Don't access any_thread_, instead use AnyThread(). |
| AnyThread any_thread_; |
| AnyThread& AnyThread() { |
| any_thread_lock_.AssertAcquired(); |
| return any_thread_; |
| } |
| const struct AnyThread& AnyThread() const { |
| any_thread_lock_.AssertAcquired(); |
| return any_thread_; |
| } |
| |
| // Don't access compositor_thread_only_, instead use CompositorThreadOnly(). |
| CompositorThreadOnly compositor_thread_only_; |
| CompositorThreadOnly& CompositorThreadOnly() { |
| compositor_thread_only_.CheckOnValidThread(); |
| return compositor_thread_only_; |
| } |
| |
| PollableThreadSafeFlag policy_may_need_update_; |
| base::WeakPtrFactory<RendererSchedulerImpl> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImpl); |
| }; |
| |
| } // namespace scheduler |
| |
| #endif // COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_ |