blob: 89ea3cb1ef992f3752c1238aa91214d11a2fd3d5 [file] [log] [blame]
// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_IMPL_H_
#include <map>
#include <memory>
#include <random>
#include <stack>
#include "base/atomicops.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/single_sample_metrics.h"
#include "base/optional.h"
#include "base/profiler/sample_metadata.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/sequence_manager/task_time_observer.h"
#include "base/trace_event/trace_log.h"
#include "build/build_config.h"
#include "components/power_scheduler/power_mode_voter.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/common/idle_helper.h"
#include "third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h"
#include "third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/use_case.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/rail_mode_observer.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
namespace base {
class TaskObserver;
} // namespace base
namespace blink {
namespace scheduler {
namespace frame_scheduler_impl_unittest {
class FrameSchedulerImplTest;
} // namespace frame_scheduler_impl_unittest
namespace main_thread_scheduler_impl_unittest {
class MainThreadSchedulerImplForTest;
class MainThreadSchedulerImplTest;
class MockPageSchedulerImpl;
FORWARD_DECLARE_TEST(MainThreadSchedulerImplTest, ShouldIgnoreTaskForUkm);
FORWARD_DECLARE_TEST(MainThreadSchedulerImplTest, Tracing);
FORWARD_DECLARE_TEST(MainThreadSchedulerImplTest,
LogIpcsPostedToDocumentsInBackForwardCache);
} // namespace main_thread_scheduler_impl_unittest
class AgentGroupSchedulerImpl;
class FrameSchedulerImpl;
class PageSchedulerImpl;
class TaskQueueThrottler;
class WebRenderWidgetSchedulingState;
class PLATFORM_EXPORT MainThreadSchedulerImpl
: public ThreadSchedulerImpl,
public AgentSchedulingStrategy::Delegate,
public IdleHelper::Delegate,
public MainThreadSchedulerHelper::Observer,
public RenderWidgetSignals::Observer,
public base::trace_event::TraceLog::AsyncEnabledStateObserver {
public:
// Don't use except for tracing.
struct TaskDescriptionForTracing {
TaskType task_type;
base::Optional<MainThreadTaskQueue::QueueType> queue_type;
// Required in order to wrap in TraceableState.
constexpr bool operator!=(const TaskDescriptionForTracing& rhs) const {
return task_type != rhs.task_type || queue_type != rhs.queue_type;
}
};
struct SchedulingSettings {
SchedulingSettings();
// Background page priority experiment (crbug.com/848835).
bool low_priority_background_page;
bool best_effort_background_page;
// Task and subframe priority experiment (crbug.com/852380).
bool low_priority_subframe;
bool low_priority_throttleable;
bool low_priority_subframe_throttleable;
bool low_priority_hidden_frame;
// Used along with |low_priority_subframe|, |low_priority_throttleable|,
// |low_priority_subframe_throttleable|, |low_priority_hidden_frame|
// to enable one of these experiments during the loading phase only.
bool use_frame_priorities_only_during_loading;
// Ads priority experiment (crbug.com/856150).
bool low_priority_ad_frame;
bool best_effort_ad_frame;
bool use_adframe_priorities_only_during_loading;
// Origin type priority experiment (crbug.com/856158).
bool low_priority_cross_origin;
bool low_priority_cross_origin_only_during_loading;
// Use resource fetch priority for resource loading tasks
// (crbug.com/860545).
bool use_resource_fetch_priority;
bool use_resource_priorities_only_during_loading;
// Prioritize compositing and loading tasks until first contentful paint.
// (crbug.com/971191)
bool prioritize_compositing_and_loading_during_early_loading;
// Prioritise one BeginMainFrame after an input task.
bool prioritize_compositing_after_input;
// Contains a mapping from net::RequestPriority to TaskQueue::QueuePriority
// when use_resource_fetch_priority is enabled.
std::array<base::sequence_manager::TaskQueue::QueuePriority,
net::RequestPrioritySize::NUM_PRIORITIES>
net_to_blink_priority;
// If enabled, base::ThreadTaskRunnerHandle::Get() and
// base::SequencedTaskRunnerHandle::Get() returns the current active
// per-ASG task runner instead of the per-thread task runner.
bool mbi_override_task_runner_handle;
// If enabled, per-AgentGroupScheduler CompositorTaskRunner will be used
// instead of per-MainThreadScheduler CompositorTaskRunner.
bool mbi_compositor_task_runner_per_agent_scheduling_group;
};
static const char* UseCaseToString(UseCase use_case);
static const char* RAILModeToString(RAILMode rail_mode);
static const char* VirtualTimePolicyToString(
PageScheduler::VirtualTimePolicy);
// If |initial_virtual_time| is specified then the scheduler will be created
// with virtual time enabled and paused with base::Time will be overridden to
// start at |initial_virtual_time|.
MainThreadSchedulerImpl(
std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager,
base::Optional<base::Time> initial_virtual_time);
~MainThreadSchedulerImpl() override;
// WebThreadScheduler implementation:
std::unique_ptr<Thread> CreateMainThread() override;
std::unique_ptr<WebWidgetScheduler> CreateWidgetScheduler() override;
// Note: this is also shared by the ThreadScheduler interface.
scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override;
scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
override;
std::unique_ptr<WebRenderWidgetSchedulingState>
NewRenderWidgetSchedulingState() override;
void WillBeginFrame(const viz::BeginFrameArgs& args) override;
void BeginFrameNotExpectedSoon() override;
void BeginMainFrameNotExpectedUntil(base::TimeTicks time) override;
void DidCommitFrameToCompositor() override;
void DidHandleInputEventOnCompositorThread(
const WebInputEvent& web_input_event,
InputEventState event_state) override;
void WillPostInputEventToMainThread(
WebInputEvent::Type web_input_event_type,
const WebInputEventAttribution& web_input_event_attribution) override;
void WillHandleInputEventOnMainThread(
WebInputEvent::Type web_input_event_type,
const WebInputEventAttribution& web_input_event_attribution) override;
void DidHandleInputEventOnMainThread(const WebInputEvent& web_input_event,
WebInputEventResult result) override;
void DidAnimateForInputOnCompositorThread() override;
void DidScheduleBeginMainFrame() override;
void DidRunBeginMainFrame() override;
void SetRendererHidden(bool hidden) override;
void SetRendererBackgrounded(bool backgrounded) override;
void SetSchedulerKeepActive(bool keep_active) override;
bool SchedulerKeepActive();
void OnMainFrameRequestedForInput() override;
#if defined(OS_ANDROID)
void PauseTimersForAndroidWebView() override;
void ResumeTimersForAndroidWebView() override;
#endif
std::unique_ptr<ThreadScheduler::RendererPauseHandle> PauseRenderer() override
WARN_UNUSED_RESULT;
bool IsHighPriorityWorkAnticipated() override;
bool ShouldYieldForHighPriorityWork() override;
bool CanExceedIdleDeadlineIfRequired() const override;
void AddTaskObserver(base::TaskObserver* task_observer) override;
void RemoveTaskObserver(base::TaskObserver* task_observer) override;
void Shutdown() override;
void SetTopLevelBlameContext(
base::trace_event::BlameContext* blame_context) override;
void AddRAILModeObserver(RAILModeObserver* observer) override;
void RemoveRAILModeObserver(RAILModeObserver const* observer) override;
void SetRendererProcessType(WebRendererProcessType type) override;
Vector<WebInputEventAttribution> GetPendingUserInputInfo(
bool include_continuous) const override;
bool IsBeginMainFrameScheduled() const override;
// ThreadScheduler implementation:
void PostIdleTask(const base::Location&, Thread::IdleTask) override;
void PostNonNestableIdleTask(const base::Location&,
Thread::IdleTask) override;
void PostDelayedIdleTask(const base::Location&,
base::TimeDelta delay,
Thread::IdleTask) override;
scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override;
scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override;
std::unique_ptr<WebAgentGroupScheduler> CreateAgentGroupScheduler() override;
WebAgentGroupScheduler* GetCurrentAgentGroupScheduler() override;
std::unique_ptr<ThreadScheduler::RendererPauseHandle> PauseScheduler()
override;
base::TimeTicks MonotonicallyIncreasingVirtualTime() override;
WebThreadScheduler* GetWebMainThreadSchedulerForTest() override;
NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
return nullptr;
}
// Use a separate task runner so that IPC tasks are not logged via the same
// task queue that executes them. Otherwise this would result in an infinite
// loop of posting and logging to a single queue.
scoped_refptr<base::SingleThreadTaskRunner>
BackForwardCacheIpcTrackingTaskRunner() {
return back_forward_cache_ipc_tracking_task_runner_;
}
// WebThreadScheduler implementation:
scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
// The following functions are defined in both WebThreadScheduler and
// ThreadScheduler, and have the same function signatures -- see above.
// This class implements those functions for both base classes.
//
// void Shutdown() override;
//
// TODO(yutak): Reduce the overlaps and simplify.
// RenderWidgetSignals::Observer implementation:
void SetAllRenderWidgetsHidden(bool hidden) override;
void SetHasVisibleRenderWidgetWithTouchHandler(
bool has_visible_render_widget_with_touch_handler) override;
// SchedulerHelper::Observer implementation:
void OnBeginNestedRunLoop() override;
void OnExitNestedRunLoop() override;
// ThreadSchedulerImpl implementation:
scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override;
scoped_refptr<base::SingleThreadTaskRunner> ControlTaskRunner() override;
void RegisterTimeDomain(
base::sequence_manager::TimeDomain* time_domain) override;
void UnregisterTimeDomain(
base::sequence_manager::TimeDomain* time_domain) override;
base::sequence_manager::TimeDomain* GetActiveTimeDomain() override;
const base::TickClock* GetTickClock() override;
scoped_refptr<base::SingleThreadTaskRunner> VirtualTimeControlTaskRunner();
// Returns a new task queue created with given params.
scoped_refptr<MainThreadTaskQueue> NewTaskQueue(
const MainThreadTaskQueue::QueueCreationParams& params);
// Returns a new loading task queue. This queue is intended for tasks related
// to resource dispatch, foreground HTML parsing, etc...
// Note: Tasks posted to kFrameLoadingControl queues must execute quickly.
scoped_refptr<MainThreadTaskQueue> NewLoadingTaskQueue(
MainThreadTaskQueue::QueueType queue_type,
FrameSchedulerImpl* frame_scheduler);
// Returns a new throttleable task queue to be used for tests.
scoped_refptr<MainThreadTaskQueue> NewThrottleableTaskQueueForTest(
FrameSchedulerImpl* frame_scheduler);
scoped_refptr<base::sequence_manager::TaskQueue> NewTaskQueueForTest();
using VirtualTimePolicy = PageScheduler::VirtualTimePolicy;
using BaseTimeOverridePolicy =
AutoAdvancingVirtualTimeDomain::BaseTimeOverridePolicy;
// Tells the scheduler that all TaskQueues should use virtual time. Depending
// on the initial time, picks the policy to be either overriding or not.
base::TimeTicks EnableVirtualTime();
// Tells the scheduler that all TaskQueues should use virtual time. Returns
// the base::TimeTicks that virtual time offsets will be relative to.
base::TimeTicks EnableVirtualTime(BaseTimeOverridePolicy policy);
bool IsVirtualTimeEnabled() const;
// Migrates all task queues to real time.
void DisableVirtualTimeForTesting();
// Returns true if virtual time is not paused.
bool VirtualTimeAllowedToAdvance() const;
void SetVirtualTimePolicy(VirtualTimePolicy virtual_time_policy);
void SetInitialVirtualTime(base::Time time);
void SetInitialVirtualTimeOffset(base::TimeDelta offset);
void SetMaxVirtualTimeTaskStarvationCount(int max_task_starvation_count);
base::TimeTicks IncrementVirtualTimePauseCount();
void DecrementVirtualTimePauseCount();
void MaybeAdvanceVirtualTime(base::TimeTicks new_virtual_time);
void RemoveAgentGroupScheduler(AgentGroupSchedulerImpl*);
void AddPageScheduler(PageSchedulerImpl*);
void RemovePageScheduler(PageSchedulerImpl*);
void OnFrameAdded(const FrameSchedulerImpl& frame_scheduler);
void OnFrameRemoved(const FrameSchedulerImpl& frame_scheduler);
AgentSchedulingStrategy& agent_scheduling_strategy() {
return *agent_scheduling_strategy_;
}
// Called by an associated PageScheduler when frozen or resumed.
void OnPageFrozen();
void OnPageResumed();
void AddTaskTimeObserver(base::sequence_manager::TaskTimeObserver*);
void RemoveTaskTimeObserver(base::sequence_manager::TaskTimeObserver*);
// Snapshots this MainThreadSchedulerImpl for tracing.
void CreateTraceEventObjectSnapshot() const;
// Called when one of associated page schedulers has changed audio state.
void OnAudioStateChanged();
// Tells the scheduler that a provisional load has committed. Must be called
// from the main thread.
void DidStartProvisionalLoad(bool is_main_frame);
// Tells the scheduler that a provisional load has committed. The scheduler
// may reset the task cost estimators and the UserModel. Must be called from
// the main thread.
void DidCommitProvisionalLoad(bool is_web_history_inert_commit,
bool is_reload,
bool is_main_frame);
// Note that the main's thread policy should be upto date to compute
// the correct priority.
base::sequence_manager::TaskQueue::QueuePriority ComputePriority(
MainThreadTaskQueue* task_queue) const;
// Test helpers.
MainThreadSchedulerHelper* GetSchedulerHelperForTesting();
IdleTimeEstimator* GetIdleTimeEstimatorForTesting();
base::TimeTicks CurrentIdleTaskDeadlineForTesting() const;
void RunIdleTasksForTesting(base::OnceClosure callback);
void EndIdlePeriodForTesting(base::OnceClosure callback,
base::TimeTicks time_remaining);
bool PolicyNeedsUpdateForTesting();
const base::TickClock* tick_clock() const;
base::sequence_manager::TimeDomain* real_time_domain() const {
return helper_.real_time_domain();
}
AutoAdvancingVirtualTimeDomain* GetVirtualTimeDomain();
TaskQueueThrottler* task_queue_throttler() const {
return task_queue_throttler_.get();
}
// Virtual for test.
virtual void OnMainFramePaint();
void OnMainFrameLoad(const FrameSchedulerImpl& frame_scheduler);
void OnAgentStrategyUpdated();
void OnShutdownTaskQueue(const scoped_refptr<MainThreadTaskQueue>& queue);
void OnTaskStarted(
MainThreadTaskQueue* queue,
const base::sequence_manager::Task& task,
const base::sequence_manager::TaskQueue::TaskTiming& task_timing);
void OnTaskCompleted(
base::WeakPtr<MainThreadTaskQueue> queue,
const base::sequence_manager::Task& task,
base::sequence_manager::TaskQueue::TaskTiming* task_timing,
base::sequence_manager::LazyNow* lazy_now);
void UpdateIpcTracking();
void SetOnIPCTaskPostedWhileInBackForwardCacheIfNeeded();
void OnIPCTaskPostedWhileInAllPagesBackForwardCache(
uint32_t ipc_hash,
const char* ipc_interface_name);
void DetachOnIPCTaskPostedWhileInBackForwardCacheHandler();
bool IsAudioPlaying() const;
// base::trace_event::TraceLog::EnabledStateObserver implementation:
void OnTraceLogEnabled() override;
void OnTraceLogDisabled() override;
UseCase current_use_case() const;
const SchedulingSettings& scheduling_settings() const;
void SetPrioritizeCompositingAfterInput(
bool prioritize_compositing_after_input);
void OnCompositorPriorityExperimentUpdateCompositorPriority();
// Allow places in the scheduler to do some work after the current task.
// The primary use case here is batching – to allow updates to be processed
// only once per task.
void ExecuteAfterCurrentTask(base::OnceClosure on_completion_task);
base::WeakPtr<MainThreadSchedulerImpl> GetWeakPtr();
base::sequence_manager::TaskQueue::QueuePriority compositor_priority() const {
return main_thread_only().compositor_priority;
}
bool should_prioritize_loading_with_compositing() const {
return main_thread_only()
.current_policy.should_prioritize_loading_with_compositing();
}
bool main_thread_compositing_is_fast() const {
return main_thread_only().main_thread_compositing_is_fast;
}
QueuePriority find_in_page_priority() const {
return main_thread_only().current_policy.find_in_page_priority();
}
protected:
scoped_refptr<MainThreadTaskQueue> ControlTaskQueue();
scoped_refptr<MainThreadTaskQueue> DefaultTaskQueue();
scoped_refptr<MainThreadTaskQueue> CompositorTaskQueue();
scoped_refptr<MainThreadTaskQueue> V8TaskQueue();
// A control task queue which also respects virtual time. Only available if
// virtual time has been enabled.
scoped_refptr<MainThreadTaskQueue> VirtualTimeControlTaskQueue();
// `current_use_case` will be overwritten by the next call to UpdatePolicy.
// Thus, this function should be only used for testing purposes.
void SetCurrentUseCaseForTest(UseCase use_case) {
main_thread_only().current_use_case = use_case;
}
void SetHaveSeenABlockingGestureForTesting(bool status);
virtual void PerformMicrotaskCheckpoint();
private:
friend class WebRenderWidgetSchedulingState;
friend class MainThreadMetricsHelper;
friend class MainThreadMetricsHelperTest;
friend class frame_scheduler_impl_unittest::FrameSchedulerImplTest;
friend class main_thread_scheduler_impl_unittest::
MainThreadSchedulerImplForTest;
friend class main_thread_scheduler_impl_unittest::MockPageSchedulerImpl;
friend class main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest;
friend class CompositorPriorityExperiments;
friend class FindInPageBudgetPoolController;
FRIEND_TEST_ALL_PREFIXES(
main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest,
ShouldIgnoreTaskForUkm);
FRIEND_TEST_ALL_PREFIXES(
main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest,
Tracing);
FRIEND_TEST_ALL_PREFIXES(
main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest,
LogIpcsPostedToDocumentsInBackForwardCache);
enum class TimeDomainType {
kReal,
kVirtual,
};
static const char* TimeDomainTypeToString(TimeDomainType domain_type);
void AddAgentGroupScheduler(AgentGroupSchedulerImpl*);
struct AgentGroupSchedulerScope {
std::unique_ptr<base::ThreadTaskRunnerHandleOverride>
thread_task_runner_handle_override;
WebAgentGroupScheduler* previous_agent_group_scheduler;
WebAgentGroupScheduler* current_agent_group_scheduler;
scoped_refptr<base::SingleThreadTaskRunner> previous_task_runner;
scoped_refptr<base::SingleThreadTaskRunner> current_task_runner;
const char* trace_event_scope_name;
void* trace_event_scope_id;
};
void BeginAgentGroupSchedulerScope(
WebAgentGroupScheduler* next_agent_group_scheduler);
void EndAgentGroupSchedulerScope();
bool IsAnyMainFrameWaitingForFirstContentfulPaint() const;
bool IsAnyMainFrameWaitingForFirstMeaningfulPaint() const;
class Policy {
DISALLOW_NEW();
public:
Policy() = default;
~Policy() = default;
RAILMode& rail_mode() { return rail_mode_; }
RAILMode rail_mode() const { return rail_mode_; }
bool& should_disable_throttling() { return should_disable_throttling_; }
bool should_disable_throttling() const {
return should_disable_throttling_;
}
bool& frozen_when_backgrounded() { return frozen_when_backgrounded_; }
bool frozen_when_backgrounded() const { return frozen_when_backgrounded_; }
bool& should_prioritize_loading_with_compositing() {
return should_prioritize_loading_with_compositing_;
}
bool should_prioritize_loading_with_compositing() const {
return should_prioritize_loading_with_compositing_;
}
bool& should_freeze_compositor_task_queue() {
return should_freeze_compositor_task_queue_;
}
bool should_freeze_compositor_task_queue() const {
return should_freeze_compositor_task_queue_;
}
bool& should_defer_task_queues() { return should_defer_task_queues_; }
bool should_defer_task_queues() const { return should_defer_task_queues_; }
bool& should_pause_task_queues() { return should_pause_task_queues_; }
bool should_pause_task_queues() const { return should_pause_task_queues_; }
bool& use_virtual_time() { return use_virtual_time_; }
bool use_virtual_time() const { return use_virtual_time_; }
bool& should_pause_task_queues_for_android_webview() {
return should_pause_task_queues_for_android_webview_;
}
bool should_pause_task_queues_for_android_webview() const {
return should_pause_task_queues_for_android_webview_;
}
base::sequence_manager::TaskQueue::QueuePriority& find_in_page_priority() {
return find_in_page_priority_;
}
base::sequence_manager::TaskQueue::QueuePriority find_in_page_priority()
const {
return find_in_page_priority_;
}
UseCase& use_case() { return use_case_; }
UseCase use_case() const { return use_case_; }
bool operator==(const Policy& other) const {
return rail_mode_ == other.rail_mode_ &&
should_disable_throttling_ == other.should_disable_throttling_ &&
frozen_when_backgrounded_ == other.frozen_when_backgrounded_ &&
should_prioritize_loading_with_compositing_ ==
other.should_prioritize_loading_with_compositing_ &&
should_freeze_compositor_task_queue_ ==
other.should_freeze_compositor_task_queue_ &&
should_defer_task_queues_ == other.should_defer_task_queues_ &&
should_pause_task_queues_ == other.should_pause_task_queues_ &&
use_virtual_time_ == other.use_virtual_time_ &&
should_pause_task_queues_for_android_webview_ ==
other.should_pause_task_queues_for_android_webview_ &&
find_in_page_priority_ == other.find_in_page_priority_ &&
use_case_ == other.use_case_;
}
bool IsQueueEnabled(MainThreadTaskQueue* task_queue) const;
TimeDomainType GetTimeDomainType() const;
void WriteIntoTracedValue(perfetto::TracedValue context) const;
private:
RAILMode rail_mode_{RAILMode::kAnimation};
bool should_disable_throttling_{false};
bool frozen_when_backgrounded_{false};
bool should_prioritize_loading_with_compositing_{false};
bool should_freeze_compositor_task_queue_{false};
bool should_defer_task_queues_{false};
bool should_pause_task_queues_{false};
bool use_virtual_time_{false};
bool should_pause_task_queues_for_android_webview_{false};
base::sequence_manager::TaskQueue::QueuePriority find_in_page_priority_{
FindInPageBudgetPoolController::kFindInPageBudgetNotExhaustedPriority};
UseCase use_case_{UseCase::kNone};
};
class TaskDurationMetricTracker;
class RendererPauseHandleImpl : public ThreadScheduler::RendererPauseHandle {
public:
explicit RendererPauseHandleImpl(MainThreadSchedulerImpl* scheduler);
~RendererPauseHandleImpl() override;
private:
MainThreadSchedulerImpl* scheduler_; // NOT OWNED
};
// AgentSchedulingStrategy::Delegate implementation:
void OnSetTimer(const FrameSchedulerImpl& frame_scheduler,
base::TimeDelta delay) override;
// 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 OnPendingTasksChanged(bool has_tasks) override;
void OnSafepointEntered() override;
void OnSafepointExited() override;
void DispatchRequestBeginMainFrameNotExpected(bool has_tasks);
void EndIdlePeriod();
// Update a policy which increases priority for the next beginMainFrame after
// an input event.
void UpdatePrioritizeCompositingAfterInputAfterTaskCompleted(
MainThreadTaskQueue* queue);
// Returns the serialized scheduler state for tracing.
void WriteIntoTracedValueLocked(perfetto::TracedValue context,
base::TimeTicks optional_now) const;
void CreateTraceEventObjectSnapshotLocked() const;
static bool ShouldPrioritizeInputEvent(const 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 in milliseconds we have to respond to user input as
// defined by RAILS.
static const int kRailsResponseTimeMillis = 50;
// 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 base::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 {
kMayEarlyOutIfPolicyUnchanged,
kForceUpdate,
};
// The implementation of UpdatePolicy & ForceUpdatePolicy. It is allowed to
// early out if |update_type| is kMayEarlyOutIfPolicyUnchanged.
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;
// An input event of some sort happened, the policy may need updating.
void UpdateForInputEventOnCompositorThread(const WebInputEvent& event,
InputEventState input_event_state);
// Notifies the per-agent scheduling strategy that an input event occurred.
void NotifyAgentSchedulerOnInputEvent();
void OnAgentStrategyDelayPassed(base::WeakPtr<const FrameSchedulerImpl>);
// 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;
// Report an intervention to all WebViews in this process.
void BroadcastIntervention(const String& message);
// Trigger an update to all task queues' priorities, throttling, and
// enabled/disabled state based on current policy. When triggered from a
// policy update, |previous_policy| should be populated with the pre-update
// policy.
void UpdateStateForAllTaskQueues(base::Optional<Policy> previous_policy);
void UpdateTaskQueueState(
MainThreadTaskQueue* task_queue,
base::sequence_manager::TaskQueue::QueueEnabledVoter*
task_queue_enabled_voter,
const Policy& old_policy,
const Policy& new_policy,
bool should_update_priority) const;
void PauseRendererImpl();
void ResumeRendererImpl();
void NotifyVirtualTimePaused();
void SetVirtualTimeStopped(bool virtual_time_stopped);
void ApplyVirtualTimePolicy();
// Pauses the timer queues by inserting a fence that blocks any tasks posted
// after this point from running. Orthogonal to PauseTimerQueue. Care must
// be taken when using this API to avoid fighting with the TaskQueueThrottler.
void VirtualTimePaused();
// Removes the fence added by VirtualTimePaused allowing timers to execute
// normally. Care must be taken when using this API to avoid fighting with the
// TaskQueueThrottler.
void VirtualTimeResumed();
// Returns true if there is a change in the main thread's policy that should
// trigger a priority update.
bool ShouldUpdateTaskQueuePriorities(Policy new_policy) const;
// Computes compositor priority based on various experiments and
// the use case. Defaults to kNormalPriority.
TaskQueue::QueuePriority ComputeCompositorPriority() const;
// Used to update the compositor priority on the main thread.
void UpdateCompositorTaskQueuePriority();
// Computes the priority for compositing based on the current use case.
// Returns nullopt if the use case does not need to set the priority.
base::Optional<TaskQueue::QueuePriority>
ComputeCompositorPriorityFromUseCase() const;
static void RunIdleTask(Thread::IdleTask, base::TimeTicks deadline);
// Probabilistically record all task metadata for the current task.
// If task belongs to a per-frame queue, this task is attributed to
// a particular Page, otherwise it's attributed to all Pages in the process.
void RecordTaskUkm(
MainThreadTaskQueue* queue,
const base::sequence_manager::Task& task,
const base::sequence_manager::TaskQueue::TaskTiming& task_timing);
UkmRecordingStatus RecordTaskUkmImpl(
MainThreadTaskQueue* queue,
const base::sequence_manager::Task& task,
const base::sequence_manager::TaskQueue::TaskTiming& task_timing,
FrameSchedulerImpl* frame_scheduler,
bool precise_attribution);
void SetNumberOfCompositingTasksToPrioritize(int number_of_tasks);
void ShutdownAllQueues();
// Dispatch the callbacks which requested to be executed after the current
// task.
void DispatchOnTaskCompletionCallbacks();
bool AllPagesFrozen() const;
// Indicates that scheduler has been shutdown.
// It should be accessed only on the main thread, but couldn't be a member
// of MainThreadOnly struct because last might be destructed before we
// have to check this flag during scheduler's destruction.
bool was_shutdown_ = false;
bool has_ipc_callback_set_ = false;
bool IsIpcTrackingEnabledForAllPages();
// This controller should be initialized before any TraceableVariables
// because they require one to initialize themselves.
TraceableVariableController tracing_controller_;
// Used for experiments on finch. On main thread instantiation, we cache
// the values of base::Feature flags using this struct, since calling
// base::Feature::IsEnabled is a relatively expensive operation.
//
// Note that it is important to keep this as the first member to ensure it is
// initialized first and can be used everywhere.
const SchedulingSettings scheduling_settings_;
std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager_;
MainThreadSchedulerHelper helper_;
scoped_refptr<MainThreadTaskQueue> idle_helper_queue_;
IdleHelper idle_helper_;
std::unique_ptr<TaskQueueThrottler> task_queue_throttler_;
RenderWidgetSignals render_widget_scheduler_signals_;
std::unique_ptr<FindInPageBudgetPoolController>
find_in_page_budget_pool_controller_;
const scoped_refptr<MainThreadTaskQueue> control_task_queue_;
const scoped_refptr<MainThreadTaskQueue> compositor_task_queue_;
scoped_refptr<MainThreadTaskQueue> virtual_time_control_task_queue_;
scoped_refptr<MainThreadTaskQueue>
back_forward_cache_ipc_tracking_task_queue_;
using TaskQueueVoterMap = std::map<
scoped_refptr<MainThreadTaskQueue>,
std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter>>;
TaskQueueVoterMap task_runners_;
scoped_refptr<MainThreadTaskQueue> v8_task_queue_;
scoped_refptr<MainThreadTaskQueue> memory_purge_task_queue_;
scoped_refptr<MainThreadTaskQueue> non_waking_task_queue_;
scoped_refptr<base::SingleThreadTaskRunner> v8_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> non_waking_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner>
back_forward_cache_ipc_tracking_task_runner_;
MemoryPurgeManager memory_purge_manager_;
// Note |virtual_time_domain_| is lazily created.
std::unique_ptr<AutoAdvancingVirtualTimeDomain> virtual_time_domain_;
NonWakingTimeDomain non_waking_time_domain_;
base::RepeatingClosure update_policy_closure_;
DeadlineTaskRunner delayed_update_policy_runner_;
CancelableClosureHolder end_renderer_hidden_idle_period_closure_;
base::RepeatingClosure notify_agent_strategy_on_input_event_closure_;
base::RepeatingCallback<void(base::WeakPtr<const FrameSchedulerImpl>)>
agent_strategy_delay_callback_;
std::unique_ptr<AgentSchedulingStrategy> agent_scheduling_strategy_ =
AgentSchedulingStrategy::Create(*this);
// We have decided to improve thread safety at the cost of some boilerplate
// (the accessors) for the following data members.
struct MainThreadOnly {
MainThreadOnly(
MainThreadSchedulerImpl* main_thread_scheduler_impl,
const base::TickClock* time_source,
base::TimeTicks now);
~MainThreadOnly();
IdleTimeEstimator idle_time_estimator;
TraceableState<UseCase, TracingCategoryName::kDefault> current_use_case;
Policy current_policy;
base::TimeTicks current_policy_expiration_time;
base::TimeTicks estimated_next_frame_begin;
base::TimeTicks current_task_start_time;
base::TimeDelta compositor_frame_interval;
TraceableCounter<base::TimeDelta, TracingCategoryName::kDebug>
longest_jank_free_task_duration;
TraceableCounter<int, TracingCategoryName::kInfo>
renderer_pause_count; // Renderer is paused if non-zero.
TraceableObjectState<RAILMode,
TracingCategoryName::kTopLevel>
rail_mode_for_tracing; // Don't use except for tracing.
TraceableState<bool, TracingCategoryName::kTopLevel> renderer_hidden;
base::Optional<base::ScopedSampleMetadata> renderer_hidden_metadata;
TraceableState<bool, TracingCategoryName::kTopLevel> renderer_backgrounded;
TraceableState<bool, TracingCategoryName::kDefault>
keep_active_fetch_or_worker;
TraceableState<bool, TracingCategoryName::kDefault>
blocking_input_expected_soon;
TraceableState<bool, TracingCategoryName::kDebug>
have_reported_blocking_intervention_in_current_policy;
TraceableState<bool, TracingCategoryName::kDebug>
have_reported_blocking_intervention_since_navigation;
TraceableState<bool, TracingCategoryName::kDebug>
has_visible_render_widget_with_touch_handler;
TraceableState<bool, TracingCategoryName::kDebug>
in_idle_period_for_testing;
TraceableState<bool, TracingCategoryName::kInfo> use_virtual_time;
TraceableState<bool, TracingCategoryName::kTopLevel> is_audio_playing;
TraceableState<bool, TracingCategoryName::kDebug>
compositor_will_send_main_frame_not_expected;
TraceableState<bool, TracingCategoryName::kDebug> has_navigated;
TraceableState<bool, TracingCategoryName::kDebug> pause_timers_for_webview;
base::TimeTicks background_status_changed_at;
HashSet<PageSchedulerImpl*> page_schedulers; // Not owned.
base::ObserverList<RAILModeObserver>::Unchecked
rail_mode_observers; // Not owned.
MainThreadMetricsHelper metrics_helper;
TraceableState<WebRendererProcessType, TracingCategoryName::kTopLevel>
process_type;
TraceableState<base::Optional<TaskDescriptionForTracing>,
TracingCategoryName::kInfo>
task_description_for_tracing; // Don't use except for tracing.
TraceableState<
base::Optional<base::sequence_manager::TaskQueue::QueuePriority>,
TracingCategoryName::kInfo>
task_priority_for_tracing; // Only used for tracing.
base::Time initial_virtual_time;
base::TimeTicks initial_virtual_time_ticks;
// This is used for cross origin navigations to account for virtual time
// advancing in the previous renderer.
base::TimeDelta initial_virtual_time_offset;
VirtualTimePolicy virtual_time_policy;
// In VirtualTimePolicy::kDeterministicLoading virtual time is only allowed
// to advance if this is zero.
int virtual_time_pause_count;
// The maximum number amount of delayed task starvation we will allow in
// VirtualTimePolicy::kAdvance or VirtualTimePolicy::kDeterministicLoading
// unless the run_loop is nested (in which case infinite starvation is
// allowed). NB a value of 0 allows infinite starvation.
int max_virtual_time_task_starvation_count;
bool virtual_time_stopped;
// Holds task queues that are currently running.
// The queue for the inmost task is at the top of stack when there are
// nested RunLoops.
std::stack<scoped_refptr<MainThreadTaskQueue>,
std::vector<scoped_refptr<MainThreadTaskQueue>>>
running_queues;
// True if a nested RunLoop is running.
bool nested_runloop;
// High-priority for compositing events after input. This will cause
// compositing events get a higher priority until the start of the next
// animation frame.
TraceableState<bool, TracingCategoryName::kDefault>
prioritize_compositing_after_input;
// List of callbacks to execute after the current task.
WTF::Vector<base::OnceClosure> on_task_completion_callbacks;
// Compositing priority experiments (crbug.com/966177).
CompositorPriorityExperiments compositor_priority_experiments;
bool main_thread_compositing_is_fast;
// Priority given to the main thread's compositor task queue. Defaults to
// kNormalPriority and is updated via UpdateCompositorTaskQueuePriority().
TraceableState<TaskQueue::QueuePriority, TracingCategoryName::kDefault>
compositor_priority;
WTF::Vector<AgentGroupSchedulerScope> agent_group_scheduler_scope_stack;
std::unique_ptr<power_scheduler::PowerModeVoter> audible_power_mode_voter;
};
struct AnyThread {
explicit AnyThread(MainThreadSchedulerImpl* main_thread_scheduler_impl);
~AnyThread();
PendingUserInput::Monitor pending_input_monitor;
base::TimeTicks last_idle_period_end_time;
base::TimeTicks fling_compositor_escalation_deadline;
UserModel user_model;
TraceableState<bool, TracingCategoryName::kInfo>
awaiting_touch_start_response;
TraceableState<bool, TracingCategoryName::kInfo> in_idle_period;
TraceableState<bool, TracingCategoryName::kInfo>
begin_main_frame_on_critical_path;
TraceableState<bool, TracingCategoryName::kInfo>
last_gesture_was_compositor_driven;
TraceableState<bool, TracingCategoryName::kInfo> default_gesture_prevented;
TraceableState<bool, TracingCategoryName::kInfo>
have_seen_a_blocking_gesture;
TraceableState<bool, TracingCategoryName::kInfo>
waiting_for_any_main_frame_contentful_paint;
TraceableState<bool, TracingCategoryName::kInfo>
waiting_for_any_main_frame_meaningful_paint;
TraceableState<bool, TracingCategoryName::kInfo>
have_seen_input_since_navigation;
TraceableCounter<uint32_t, TracingCategoryName::kInfo>
begin_main_frame_scheduled_count;
};
struct CompositorThreadOnly {
CompositorThreadOnly();
~CompositorThreadOnly();
WebInputEvent::Type last_input_type;
std::unique_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 main_thread_only().
MainThreadOnly main_thread_only_;
MainThreadOnly& main_thread_only() {
helper_.CheckOnValidThread();
return main_thread_only_;
}
const struct MainThreadOnly& main_thread_only() const {
helper_.CheckOnValidThread();
return main_thread_only_;
}
mutable base::Lock any_thread_lock_;
// Don't access any_thread_, instead use any_thread().
AnyThread any_thread_;
AnyThread& any_thread() {
any_thread_lock_.AssertAcquired();
return any_thread_;
}
const struct AnyThread& any_thread() const {
any_thread_lock_.AssertAcquired();
return any_thread_;
}
// Don't access compositor_thread_only_, instead use
// |GetCompositorThreadOnly()|.
CompositorThreadOnly compositor_thread_only_;
CompositorThreadOnly& GetCompositorThreadOnly() {
compositor_thread_only_.CheckOnValidThread();
return compositor_thread_only_;
}
PollableThreadSafeFlag policy_may_need_update_;
PollableThreadSafeFlag notify_agent_strategy_task_posted_;
WTF::HashSet<AgentGroupSchedulerImpl*> agent_group_schedulers_;
WebAgentGroupScheduler* current_agent_group_scheduler_{nullptr};
base::WeakPtrFactory<MainThreadSchedulerImpl> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImpl);
};
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_IMPL_H_