diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc | 532 |
1 files changed, 288 insertions, 244 deletions
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc index f94bcb88e6d..57b546b0536 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc @@ -5,20 +5,24 @@ #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h" #include <memory> +#include <set> +#include <string> +#include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/blame_context.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/blame_context.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/scheduler/child/features.h" -#include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.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/main_thread_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h" #include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h" #include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h" @@ -27,6 +31,7 @@ namespace blink { namespace scheduler { using base::sequence_manager::TaskQueue; +using QueueTraits = MainThreadTaskQueue::QueueTraits; namespace { @@ -73,6 +78,44 @@ void UpdatePriority(MainThreadTaskQueue* task_queue) { task_queue->SetQueuePriority(frame_scheduler->ComputePriority(task_queue)); } +// Extract a substring from |source| from [start to end), trimming leading +// whitespace. +std::string ExtractAndTrimString(std::string source, size_t start, size_t end) { + DCHECK(start < source.length()); + DCHECK(end <= source.length()); + DCHECK(start <= end); + // Trim whitespace + while (start < end && source[start] == ' ') + ++start; + if (start < end) + return source.substr(start, end - start); + return ""; +} + +std::set<std::string> TaskTypesFromFieldTrialParam(const char* param) { + std::set<std::string> result; + std::string task_type_list = base::GetFieldTrialParamValueByFeature( + kThrottleAndFreezeTaskTypes, param); + if (!task_type_list.length()) + return result; + // Extract the individual names, separated by ",". + size_t pos = 0, start = 0; + while ((pos = task_type_list.find(',', start)) != std::string::npos) { + std::string task_type = ExtractAndTrimString(task_type_list, start, pos); + // Not valid to start with "," or have ",," in the list. + DCHECK(task_type.length()); + result.insert(task_type); + start = pos + 1; + } + // Handle the last or only task type name. + std::string task_type = + ExtractAndTrimString(task_type_list, start, task_type_list.length()); + DCHECK(task_type.length()); + result.insert(task_type); + + return result; +} + } // namespace FrameSchedulerImpl::ActiveConnectionHandleImpl::ActiveConnectionHandleImpl( @@ -104,11 +147,12 @@ FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl:: std::unique_ptr<FrameSchedulerImpl> FrameSchedulerImpl::Create( PageSchedulerImpl* parent_page_scheduler, + FrameScheduler::Delegate* delegate, base::trace_event::BlameContext* blame_context, FrameScheduler::FrameType frame_type) { - std::unique_ptr<FrameSchedulerImpl> frame_scheduler( - new FrameSchedulerImpl(parent_page_scheduler->GetMainThreadScheduler(), - parent_page_scheduler, blame_context, frame_type)); + std::unique_ptr<FrameSchedulerImpl> frame_scheduler(new FrameSchedulerImpl( + parent_page_scheduler->GetMainThreadScheduler(), parent_page_scheduler, + delegate, blame_context, frame_type)); parent_page_scheduler->RegisterFrameSchedulerImpl(frame_scheduler.get()); return frame_scheduler; } @@ -116,12 +160,14 @@ std::unique_ptr<FrameSchedulerImpl> FrameSchedulerImpl::Create( FrameSchedulerImpl::FrameSchedulerImpl( MainThreadSchedulerImpl* main_thread_scheduler, PageSchedulerImpl* parent_page_scheduler, + FrameScheduler::Delegate* delegate, base::trace_event::BlameContext* blame_context, FrameScheduler::FrameType frame_type) : frame_type_(frame_type), is_ad_frame_(false), main_thread_scheduler_(main_thread_scheduler), parent_page_scheduler_(parent_page_scheduler), + delegate_(delegate), blame_context_(blame_context), throttling_state_(SchedulingLifecycleState::kNotThrottled), frame_visible_(true, @@ -147,11 +193,11 @@ FrameSchedulerImpl::FrameSchedulerImpl( &tracing_controller_, PausedStateToString), url_tracer_("FrameScheduler.URL", this), - task_queue_throttled_(false, - "FrameScheduler.TaskQueueThrottled", - this, - &tracing_controller_, - YesNoStateToString), + task_queues_throttled_(false, + "FrameScheduler.TaskQueuesThrottled", + this, + &tracing_controller_, + YesNoStateToString), active_connection_count_(0), subresource_loading_pause_count_(0u), has_active_connection_(false, @@ -179,16 +225,23 @@ FrameSchedulerImpl::FrameSchedulerImpl( this, &tracing_controller_, KeepActiveStateToString), - weak_factory_(this) {} + weak_factory_(this) { + frame_task_queue_controller_.reset( + new FrameTaskQueueController(main_thread_scheduler_, this, this)); +} FrameSchedulerImpl::FrameSchedulerImpl() - : FrameSchedulerImpl(nullptr, nullptr, nullptr, FrameType::kSubframe) {} + : FrameSchedulerImpl(nullptr, + nullptr, + nullptr, + nullptr, + FrameType::kSubframe) {} namespace { void CleanUpQueue(MainThreadTaskQueue* queue) { - if (!queue) - return; + DCHECK(queue); + queue->DetachFromMainThreadScheduler(); queue->DetachFromFrameScheduler(); queue->SetBlameContext(nullptr); @@ -200,17 +253,13 @@ void CleanUpQueue(MainThreadTaskQueue* queue) { FrameSchedulerImpl::~FrameSchedulerImpl() { weak_factory_.InvalidateWeakPtrs(); - RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(); - - CleanUpQueue(loading_task_queue_.get()); - CleanUpQueue(loading_control_task_queue_.get()); - CleanUpQueue(throttleable_task_queue_.get()); - CleanUpQueue(deferrable_task_queue_.get()); - CleanUpQueue(pausable_task_queue_.get()); - CleanUpQueue(unpausable_task_queue_.get()); - - for (const auto& pair : resource_loading_task_queues_) { - CleanUpQueue(pair.key.get()); + for (const auto& task_queue_and_voter : + frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) { + if (task_queue_and_voter.first->CanBeThrottled()) { + RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool( + task_queue_and_voter.first); + } + CleanUpQueue(task_queue_and_voter.first); } if (parent_page_scheduler_) { @@ -222,15 +271,21 @@ FrameSchedulerImpl::~FrameSchedulerImpl() { } void FrameSchedulerImpl::DetachFromPageScheduler() { - RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(); + for (const auto& task_queue_and_voter : + frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) { + if (task_queue_and_voter.first->CanBeThrottled()) { + RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool( + task_queue_and_voter.first); + } + } parent_page_scheduler_ = nullptr; } -void FrameSchedulerImpl:: - RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool() { - if (!throttleable_task_queue_) - return; +void FrameSchedulerImpl::RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool( + MainThreadTaskQueue* task_queue) { + DCHECK(task_queue); + DCHECK(task_queue->CanBeThrottled()); if (!parent_page_scheduler_) return; @@ -241,9 +296,14 @@ void FrameSchedulerImpl:: if (!time_budget_pool) return; - time_budget_pool->RemoveQueue( - main_thread_scheduler_->tick_clock()->NowTicks(), - throttleable_task_queue_.get()); + // On tests, the scheduler helper might already be shut down and tick is not + // available. + base::TimeTicks now; + if (main_thread_scheduler_->tick_clock()) + now = main_thread_scheduler_->tick_clock()->NowTicks(); + else + now = base::TimeTicks::Now(); + time_budget_pool->RemoveQueue(now, task_queue); } void FrameSchedulerImpl::SetFrameVisible(bool frame_visible) { @@ -294,18 +354,52 @@ FrameScheduler::FrameType FrameSchedulerImpl::GetFrameType() const { return frame_type_; } -scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( +void FrameSchedulerImpl::InitializeTaskTypeQueueTraitsMap( + FrameTaskTypeToQueueTraitsArray& frame_task_types_to_queue_traits) { + DCHECK_EQ(frame_task_types_to_queue_traits.size(), + static_cast<size_t>(TaskType::kCount)); + // Using std set and strings here because field trial parameters are std + // strings, and we cannot use WTF strings as Blink is not yet initialized. + std::set<std::string> throttleable_task_type_names; + std::set<std::string> freezable_task_type_names; + if (base::FeatureList::IsEnabled(kThrottleAndFreezeTaskTypes)) { + throttleable_task_type_names = + TaskTypesFromFieldTrialParam(kThrottleableTaskTypesListParam); + freezable_task_type_names = + TaskTypesFromFieldTrialParam(kFreezableTaskTypesListParam); + } + for (size_t i = 0; i < static_cast<size_t>(TaskType::kCount); i++) { + TaskType type = static_cast<TaskType>(i); + base::Optional<QueueTraits> queue_traits = + CreateQueueTraitsForTaskType(type); + if (queue_traits && (throttleable_task_type_names.size() || + freezable_task_type_names.size())) { + const char* task_type_name = TaskTypeNames::TaskTypeToString(type); + if (throttleable_task_type_names.erase(task_type_name)) + queue_traits->SetCanBeThrottled(true); + if (freezable_task_type_names.erase(task_type_name)) + queue_traits->SetCanBeFrozen(true); + } + frame_task_types_to_queue_traits[i] = queue_traits; + } + // Protect against configuration errors. + DCHECK(throttleable_task_type_names.empty()); + DCHECK(freezable_task_type_names.empty()); +} + +// static +base::Optional<QueueTraits> FrameSchedulerImpl::CreateQueueTraitsForTaskType( TaskType type) { // TODO(haraken): Optimize the mapping from TaskTypes to task runners. switch (type) { case TaskType::kJavascriptTimer: - return TaskQueueWithTaskType::Create(ThrottleableTaskQueue(), type); + return ThrottleableTaskQueueTraits(); case TaskType::kInternalLoading: case TaskType::kNetworking: case TaskType::kNetworkingWithURLLoaderAnnotation: - return TaskQueueWithTaskType::Create(LoadingTaskQueue(), type); case TaskType::kNetworkingControl: - return TaskQueueWithTaskType::Create(LoadingControlTaskQueue(), type); + // Loading task queues are handled separately. + return base::nullopt; // Throttling following tasks may break existing web pages, so tentatively // these are unthrottled. // TODO(nhiroki): Throttle them again after we're convinced that it's safe @@ -329,7 +423,7 @@ scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( case TaskType::kInternalDefault: case TaskType::kMiscPlatformAPI: // TODO(altimin): Move appropriate tasks to throttleable task queue. - return TaskQueueWithTaskType::Create(DeferrableTaskQueue(), type); + return DeferrableTaskQueueTraits(); // PostedMessage can be used for navigation, so we shouldn't defer it // when expecting a user gesture. case TaskType::kPostedMessage: @@ -346,7 +440,7 @@ scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( case TaskType::kInternalMediaRealTime: case TaskType::kInternalUserInteraction: case TaskType::kInternalIntersectionObserver: - return TaskQueueWithTaskType::Create(PausableTaskQueue(), type); + return PausableTaskQueueTraits(); case TaskType::kInternalIPC: // The TaskType of Inspector tasks needs to be unpausable because they need // to run even on a paused page. @@ -355,7 +449,7 @@ scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( // unthrottled and undeferred) not to prevent service workers that may // control browser navigation on multiple tabs. case TaskType::kInternalWorker: - return TaskQueueWithTaskType::Create(UnpausableTaskQueue(), type); + return UnpausableTaskQueueTraits(); case TaskType::kDeprecatedNone: case TaskType::kMainThreadTaskQueueV8: case TaskType::kMainThreadTaskQueueCompositor: @@ -364,41 +458,53 @@ scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( case TaskType::kMainThreadTaskQueueIdle: case TaskType::kMainThreadTaskQueueIPC: case TaskType::kMainThreadTaskQueueControl: + case TaskType::kMainThreadTaskQueueCleanup: case TaskType::kCompositorThreadTaskQueueDefault: case TaskType::kCompositorThreadTaskQueueInput: case TaskType::kWorkerThreadTaskQueueDefault: case TaskType::kWorkerThreadTaskQueueV8: case TaskType::kWorkerThreadTaskQueueCompositor: case TaskType::kCount: - NOTREACHED(); - break; + // Not a valid frame-level TaskType. + return base::nullopt; } - NOTREACHED(); - return nullptr; + // This method is called for all values between 0 and kCount. TaskType, + // however, has numbering gaps, so even though all enumerated TaskTypes are + // handled in the switch and return a value, we fall through for some values + // of |type|. + return base::nullopt; } -std::pair<scoped_refptr<MainThreadTaskQueue>, - std::unique_ptr<TaskQueue::QueueEnabledVoter>> -FrameSchedulerImpl::CreateNewLoadingTaskQueue() { - // TODO(panicker): Avoid adding this queue in RS task_runners_. - scoped_refptr<MainThreadTaskQueue> loading_task_queue = - main_thread_scheduler_->NewLoadingTaskQueue( - MainThreadTaskQueue::QueueType::kFrameLoading, this); - loading_task_queue->SetBlameContext(blame_context_); - std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = - loading_task_queue->CreateQueueEnabledVoter(); - voter->SetQueueEnabled(!frame_paused_); - return std::make_pair(loading_task_queue, std::move(voter)); +scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( + TaskType type) { + scoped_refptr<MainThreadTaskQueue> task_queue = GetTaskQueue(type); + DCHECK(task_queue); + return task_queue->CreateTaskRunner(type); } -scoped_refptr<TaskQueue> FrameSchedulerImpl::LoadingTaskQueue() { - DCHECK(parent_page_scheduler_); - if (!loading_task_queue_) { - auto queue_voter_pair = CreateNewLoadingTaskQueue(); - loading_task_queue_ = queue_voter_pair.first; - loading_queue_enabled_voter_ = std::move(queue_voter_pair.second); +scoped_refptr<MainThreadTaskQueue> FrameSchedulerImpl::GetTaskQueue( + TaskType type) { + switch (type) { + case TaskType::kInternalLoading: + case TaskType::kNetworking: + case TaskType::kNetworkingWithURLLoaderAnnotation: + return frame_task_queue_controller_->LoadingTaskQueue(); + case TaskType::kNetworkingControl: + return frame_task_queue_controller_->LoadingControlTaskQueue(); + default: + // Non-loading task queue. + DCHECK_LT(static_cast<size_t>(type), + main_thread_scheduler_->scheduling_settings() + .frame_task_types_to_queue_traits.size()); + base::Optional<QueueTraits> queue_traits = + main_thread_scheduler_->scheduling_settings() + .frame_task_types_to_queue_traits[static_cast<size_t>(type)]; + // We don't have a QueueTraits mapping for |task_type| if it is not a + // frame-level task type. + DCHECK(queue_traits); + return frame_task_queue_controller_->NonLoadingTaskQueue( + queue_traits.value()); } - return loading_task_queue_; } std::unique_ptr<WebResourceLoadingTaskRunnerHandle> @@ -413,143 +519,51 @@ FrameSchedulerImpl::CreateResourceLoadingTaskRunnerHandleImpl() { (parent_page_scheduler_->IsLoading() && main_thread_scheduler_->scheduling_settings() .use_resource_priorities_only_during_loading)) { - auto queue_voter_pair = CreateNewLoadingTaskQueue(); - - resource_loading_task_queues_.insert( - queue_voter_pair.first, ResourceLoadingTaskQueueMetadata( - queue_voter_pair.first->GetQueuePriority(), - std::move(queue_voter_pair.second))); - - return ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner( - queue_voter_pair.first); + scoped_refptr<MainThreadTaskQueue> task_queue = + frame_task_queue_controller_->NewResourceLoadingTaskQueue(); + resource_loading_task_queue_priorities_.insert( + task_queue, task_queue->GetQueuePriority()); + return ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner(task_queue); } - // Make sure the loading task queue exists. - LoadingTaskQueue(); return ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner( - loading_task_queue_); + frame_task_queue_controller_->LoadingTaskQueue()); } void FrameSchedulerImpl::DidChangeResourceLoadingPriority( scoped_refptr<MainThreadTaskQueue> task_queue, net::RequestPriority priority) { // This check is done since in some cases (when kUseResourceFetchPriority - // feature isn't enabled) we use |loading_task_queue_| for resource loading + // feature isn't enabled) we use the loading task queue for resource loading // and the priority of this queue shouldn't be affected by resource // priorities. - auto queue_metadata_pair = resource_loading_task_queues_.find(task_queue); - if (queue_metadata_pair != resource_loading_task_queues_.end()) { - queue_metadata_pair->value.priority = - main_thread_scheduler_->scheduling_settings() - .net_to_blink_priority[priority]; - UpdateQueuePolicy(task_queue.get(), queue_metadata_pair->value.voter.get()); + auto queue_priority_pair = + resource_loading_task_queue_priorities_.find(task_queue); + if (queue_priority_pair != resource_loading_task_queue_priorities_.end()) { + task_queue->SetNetRequestPriority(priority); + queue_priority_pair->value = main_thread_scheduler_->scheduling_settings() + .net_to_blink_priority[priority]; + auto* voter = + frame_task_queue_controller_->GetQueueEnabledVoter(task_queue); + UpdateQueuePolicy(task_queue.get(), voter); } } void FrameSchedulerImpl::OnShutdownResourceLoadingTaskQueue( scoped_refptr<MainThreadTaskQueue> task_queue) { // This check is done since in some cases (when kUseResourceFetchPriority - // feature isn't enabled) we use |loading_task_queue_| for resource loading, + // feature isn't enabled) we use the loading task queue for resource loading, // and the lifetime of this queue isn't bound to one resource. - auto iter = resource_loading_task_queues_.find(task_queue); - if (iter != resource_loading_task_queues_.end()) { - resource_loading_task_queues_.erase(iter); + auto iter = resource_loading_task_queue_priorities_.find(task_queue); + if (iter != resource_loading_task_queue_priorities_.end()) { + resource_loading_task_queue_priorities_.erase(iter); + bool removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue( + task_queue); + DCHECK(removed); CleanUpQueue(task_queue.get()); } } -scoped_refptr<TaskQueue> FrameSchedulerImpl::LoadingControlTaskQueue() { - DCHECK(parent_page_scheduler_); - if (!loading_control_task_queue_) { - loading_control_task_queue_ = main_thread_scheduler_->NewLoadingTaskQueue( - MainThreadTaskQueue::QueueType::kFrameLoadingControl, this); - loading_control_task_queue_->SetBlameContext(blame_context_); - loading_control_queue_enabled_voter_ = - loading_control_task_queue_->CreateQueueEnabledVoter(); - loading_control_queue_enabled_voter_->SetQueueEnabled(!frame_paused_); - } - return loading_control_task_queue_; -} - -scoped_refptr<TaskQueue> FrameSchedulerImpl::ThrottleableTaskQueue() { - DCHECK(parent_page_scheduler_); - if (!throttleable_task_queue_) { - // TODO(panicker): Avoid adding this queue in RS task_runners_. - throttleable_task_queue_ = main_thread_scheduler_->NewTaskQueue( - MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kFrameThrottleable) - .SetCanBeThrottled(true) - .SetCanBeFrozen(true) - .SetFreezeWhenKeepActive(true) - .SetCanBeDeferred(true) - .SetCanBePaused(true) - .SetFrameScheduler(this)); - throttleable_task_queue_->SetBlameContext(blame_context_); - throttleable_queue_enabled_voter_ = - throttleable_task_queue_->CreateQueueEnabledVoter(); - throttleable_queue_enabled_voter_->SetQueueEnabled(!frame_paused_); - - CPUTimeBudgetPool* time_budget_pool = - parent_page_scheduler_->BackgroundCPUTimeBudgetPool(); - if (time_budget_pool) { - time_budget_pool->AddQueue( - main_thread_scheduler_->tick_clock()->NowTicks(), - throttleable_task_queue_.get()); - } - UpdateThrottling(); - } - return throttleable_task_queue_; -} - -scoped_refptr<TaskQueue> FrameSchedulerImpl::DeferrableTaskQueue() { - DCHECK(parent_page_scheduler_); - if (!deferrable_task_queue_) { - deferrable_task_queue_ = main_thread_scheduler_->NewTaskQueue( - MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kFrameDeferrable) - .SetCanBeDeferred(true) - .SetCanBeFrozen( - RuntimeEnabledFeatures::StopNonTimersInBackgroundEnabled()) - .SetCanBePaused(true) - .SetFrameScheduler(this)); - deferrable_task_queue_->SetBlameContext(blame_context_); - deferrable_queue_enabled_voter_ = - deferrable_task_queue_->CreateQueueEnabledVoter(); - deferrable_queue_enabled_voter_->SetQueueEnabled(!frame_paused_); - } - return deferrable_task_queue_; -} - -scoped_refptr<TaskQueue> FrameSchedulerImpl::PausableTaskQueue() { - DCHECK(parent_page_scheduler_); - if (!pausable_task_queue_) { - pausable_task_queue_ = main_thread_scheduler_->NewTaskQueue( - MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kFramePausable) - .SetCanBeFrozen( - RuntimeEnabledFeatures::StopNonTimersInBackgroundEnabled()) - .SetCanBePaused(true) - .SetFrameScheduler(this)); - pausable_task_queue_->SetBlameContext(blame_context_); - pausable_queue_enabled_voter_ = - pausable_task_queue_->CreateQueueEnabledVoter(); - pausable_queue_enabled_voter_->SetQueueEnabled(!frame_paused_); - } - return pausable_task_queue_; -} - -scoped_refptr<TaskQueue> FrameSchedulerImpl::UnpausableTaskQueue() { - DCHECK(parent_page_scheduler_); - if (!unpausable_task_queue_) { - unpausable_task_queue_ = main_thread_scheduler_->NewTaskQueue( - MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kFrameUnpausable) - .SetFrameScheduler(this)); - unpausable_task_queue_->SetBlameContext(blame_context_); - } - return unpausable_task_queue_; -} - scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::ControlTaskRunner() { DCHECK(parent_page_scheduler_); @@ -605,36 +619,10 @@ void FrameSchedulerImpl::AsValueInto( state->SetBoolean( "disable_background_timer_throttling", !RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled()); - if (loading_task_queue_) { - state->SetString("loading_task_queue", - PointerToString(loading_task_queue_.get())); - } - if (loading_control_task_queue_) { - state->SetString("loading_control_task_queue", - PointerToString(loading_control_task_queue_.get())); - } - if (throttleable_task_queue_) { - state->SetString("throttleable_task_queue", - PointerToString(throttleable_task_queue_.get())); - } - if (deferrable_task_queue_) { - state->SetString("deferrable_task_queue", - PointerToString(deferrable_task_queue_.get())); - } - if (pausable_task_queue_) { - state->SetString("pausable_task_queue", - PointerToString(pausable_task_queue_.get())); - } - if (unpausable_task_queue_) { - state->SetString("unpausable_task_queue", - PointerToString(unpausable_task_queue_.get())); - } - state->BeginArray("resource_loading_task_queues"); - for (const auto& queue : resource_loading_task_queues_) { - state->AppendString(PointerToString(queue.key.get())); - } - state->EndArray(); + state->BeginDictionary("frame_task_queue_controller"); + frame_task_queue_controller_->AsValueInto(state); + state->EndDictionary(); if (blame_context_) { state->BeginDictionary("blame_context"); @@ -679,37 +667,33 @@ void FrameSchedulerImpl::SetPageKeepActiveForTracing(bool keep_active) { } void FrameSchedulerImpl::UpdatePolicy() { - // Per-frame (stoppable) task queues will be frozen after 5mins in - // background. They will be resumed when the page is visible. - UpdateQueuePolicy(throttleable_task_queue_, - throttleable_queue_enabled_voter_.get()); - UpdateQueuePolicy(loading_task_queue_, loading_queue_enabled_voter_.get()); - UpdateQueuePolicy(loading_control_task_queue_, - loading_control_queue_enabled_voter_.get()); - UpdateQueuePolicy(deferrable_task_queue_, - deferrable_queue_enabled_voter_.get()); - UpdateQueuePolicy(pausable_task_queue_, pausable_queue_enabled_voter_.get()); - UpdateQueuePolicy(unpausable_task_queue_, nullptr); - - for (const auto& pair : resource_loading_task_queues_) { - UpdateQueuePolicy(pair.key, pair.value.voter.get()); + bool task_queues_were_throttled = task_queues_throttled_; + task_queues_throttled_ = ShouldThrottleTaskQueues(); + + for (const auto& task_queue_and_voter : + frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) { + UpdateQueuePolicy(task_queue_and_voter.first, task_queue_and_voter.second); + if (task_queues_were_throttled != task_queues_throttled_) { + UpdateTaskQueueThrottling(task_queue_and_voter.first, + task_queues_throttled_); + } } - UpdateThrottling(); - NotifyLifecycleObservers(); } void FrameSchedulerImpl::UpdateQueuePolicy( - const scoped_refptr<MainThreadTaskQueue>& queue, + MainThreadTaskQueue* queue, TaskQueue::QueueEnabledVoter* voter) { - if (!queue) - return; - UpdatePriority(queue.get()); + DCHECK(queue); + UpdatePriority(queue); if (!voter) return; DCHECK(parent_page_scheduler_); bool queue_paused = frame_paused_ && queue->CanBePaused(); + // Per-frame freezable task queues will be frozen after 5 mins in background + // on Android, and if the browser freezes the page in the background. They + // will be resumed when the page is visible. bool queue_frozen = parent_page_scheduler_->IsFrozen() && queue->CanBeFrozen(); // Override freezing if keep-active is true. @@ -751,7 +735,7 @@ FrameSchedulerImpl::OnActiveConnectionCreated() { return std::make_unique<FrameSchedulerImpl::ActiveConnectionHandleImpl>(this); } -bool FrameSchedulerImpl::ShouldThrottleTimers() const { +bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const { if (!RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled()) return false; if (parent_page_scheduler_ && parent_page_scheduler_->IsAudioPlaying()) @@ -762,24 +746,17 @@ bool FrameSchedulerImpl::ShouldThrottleTimers() const { !frame_visible_ && IsCrossOrigin(); } -void FrameSchedulerImpl::UpdateThrottling() { - // Before we initialize a trottleable task queue, |task_queue_throttled_| - // stays false and this function ensures it indicates whether are we holding - // a queue reference for throttler or not. - // Don't modify that value neither amend the reference counter anywhere else. - if (!throttleable_task_queue_) +void FrameSchedulerImpl::UpdateTaskQueueThrottling( + MainThreadTaskQueue* task_queue, + bool should_throttle) { + if (!task_queue->CanBeThrottled()) return; - bool should_throttle = ShouldThrottleTimers(); - if (task_queue_throttled_ == should_throttle) - return; - task_queue_throttled_ = should_throttle; - if (should_throttle) { main_thread_scheduler_->task_queue_throttler()->IncreaseThrottleRefCount( - throttleable_task_queue_.get()); + task_queue); } else { main_thread_scheduler_->task_queue_throttler()->DecreaseThrottleRefCount( - throttleable_task_queue_.get()); + task_queue); } } @@ -796,10 +773,10 @@ TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority( // Checks the task queue is associated with this frame scheduler. DCHECK_EQ(frame_scheduler, this); - auto queue_metadata_pair = - resource_loading_task_queues_.find(base::WrapRefCounted(task_queue)); - if (queue_metadata_pair != resource_loading_task_queues_.end()) { - return queue_metadata_pair->value.priority; + auto queue_priority_pair = resource_loading_task_queue_priorities_.find( + base::WrapRefCounted(task_queue)); + if (queue_priority_pair != resource_loading_task_queue_priorities_.end()) { + return queue_priority_pair->value; } base::Optional<TaskQueue::QueuePriority> fixed_priority = @@ -911,5 +888,72 @@ void FrameSchedulerImpl::RemovePauseSubresourceLoadingHandle() { } } +ukm::UkmRecorder* FrameSchedulerImpl::GetUkmRecorder() { + if (!delegate_) + return nullptr; + return delegate_->GetUkmRecorder(); +} + +ukm::SourceId FrameSchedulerImpl::GetUkmSourceId() { + if (!delegate_) + return ukm::kInvalidSourceId; + return delegate_->GetUkmSourceId(); +} + +void FrameSchedulerImpl::OnTaskQueueCreated( + MainThreadTaskQueue* task_queue, + base::sequence_manager::TaskQueue::QueueEnabledVoter* voter) { + DCHECK(parent_page_scheduler_); + + task_queue->SetBlameContext(blame_context_); + UpdateQueuePolicy(task_queue, voter); + + if (task_queue->CanBeThrottled()) { + CPUTimeBudgetPool* time_budget_pool = + parent_page_scheduler_->BackgroundCPUTimeBudgetPool(); + if (time_budget_pool) { + time_budget_pool->AddQueue( + main_thread_scheduler_->tick_clock()->NowTicks(), task_queue); + } + if (task_queues_throttled_) { + UpdateTaskQueueThrottling(task_queue, true); + } + } +} + +// static +MainThreadTaskQueue::QueueTraits +FrameSchedulerImpl::ThrottleableTaskQueueTraits() { + return QueueTraits() + .SetCanBeThrottled(true) + .SetCanBeFrozen(true) + .SetCanBeDeferred(true) + .SetCanBePaused(true); +} + +// static +MainThreadTaskQueue::QueueTraits +FrameSchedulerImpl::DeferrableTaskQueueTraits() { + return QueueTraits() + .SetCanBeDeferred(true) + .SetCanBeFrozen(base::FeatureList::IsEnabled( + blink::features::kStopNonTimersInBackground)) + .SetCanBePaused(true); +} + +// static +MainThreadTaskQueue::QueueTraits FrameSchedulerImpl::PausableTaskQueueTraits() { + return QueueTraits() + .SetCanBeFrozen(base::FeatureList::IsEnabled( + blink::features::kStopNonTimersInBackground)) + .SetCanBePaused(true); +} + +// static +MainThreadTaskQueue::QueueTraits +FrameSchedulerImpl::UnpausableTaskQueueTraits() { + return QueueTraits(); +} + } // namespace scheduler } // namespace blink |