// 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 main_task_runner); ~RendererSchedulerImpl() override; // RendererScheduler implementation: scoped_ptr CreateMainThread() override; scoped_refptr DefaultTaskRunner() override; scoped_refptr IdleTaskRunner() override; scoped_refptr CompositorTaskRunner() override; scoped_refptr LoadingTaskRunner() override; scoped_refptr TimerTaskRunner() override; scoped_refptr NewLoadingTaskRunner(const char* name) override; scoped_refptr NewTimerTaskRunner(const char* name) override; scoped_ptr 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& 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 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_ptr AsValue( base::TimeTicks optional_now) const; scoped_ptr 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 throttling_helper_; RenderWidgetSignals render_widget_scheduler_signals_; const scoped_refptr control_task_runner_; const scoped_refptr compositor_task_runner_; std::set> loading_task_runners_; std::set> timer_task_runners_; scoped_refptr default_loading_task_runner_; scoped_refptr 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& 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 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 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 weak_factory_; DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImpl); }; } // namespace scheduler #endif // COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_