| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/scheduler/browser_task_queues.h" |
| |
| #include <iterator> |
| |
| #include "base/check.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/task/sequence_manager/sequence_manager.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "content/browser/scheduler/browser_task_priority.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/content_features.h" |
| |
| namespace content { |
| namespace { |
| |
| using BrowserTaskPriority = ::content::internal::BrowserTaskPriority; |
| using QueueName = ::perfetto::protos::pbzero::SequenceManagerTask::QueueName; |
| using InsertFencePosition = |
| ::base::sequence_manager::TaskQueue::InsertFencePosition; |
| using QueueEnabledVoter = base::sequence_manager::TaskQueue::QueueEnabledVoter; |
| |
| QueueName GetControlTaskQueueName(BrowserThread::ID thread_id) { |
| switch (thread_id) { |
| case BrowserThread::UI: |
| return QueueName::UI_CONTROL_TQ; |
| case BrowserThread::IO: |
| return QueueName::IO_CONTROL_TQ; |
| case BrowserThread::ID_COUNT: |
| break; |
| } |
| NOTREACHED(); |
| } |
| |
| QueueName GetRunAllPendingTaskQueueName(BrowserThread::ID thread_id) { |
| switch (thread_id) { |
| case BrowserThread::UI: |
| return QueueName::UI_RUN_ALL_PENDING_TQ; |
| case BrowserThread::IO: |
| return QueueName::IO_RUN_ALL_PENDING_TQ; |
| case BrowserThread::ID_COUNT: |
| break; |
| } |
| NOTREACHED(); |
| } |
| |
| QueueName GetUITaskQueueName(BrowserTaskQueues::QueueType queue_type) { |
| switch (queue_type) { |
| case BrowserTaskQueues::QueueType::kBestEffort: |
| return QueueName::UI_BEST_EFFORT_TQ; |
| case BrowserTaskQueues::QueueType::kDefault: |
| return QueueName::UI_DEFAULT_TQ; |
| case BrowserTaskQueues::QueueType::kUserBlocking: |
| return QueueName::UI_USER_BLOCKING_TQ; |
| case BrowserTaskQueues::QueueType::kUserVisible: |
| return QueueName::UI_USER_VISIBLE_TQ; |
| case BrowserTaskQueues::QueueType::kUserInput: |
| return QueueName::UI_USER_INPUT_TQ; |
| case BrowserTaskQueues::QueueType::kNavigationNetworkResponse: |
| return QueueName::UI_NAVIGATION_NETWORK_RESPONSE_TQ; |
| case BrowserTaskQueues::QueueType::kServiceWorkerStorageControlResponse: |
| return QueueName::UI_SERVICE_WORKER_STORAGE_CONTROL_RESPONSE_TQ; |
| case BrowserTaskQueues::QueueType::kBeforeUnloadBrowserResponse: |
| return QueueName::UI_BEFORE_UNLOAD_BROWSER_RESPONSE_TQ; |
| case BrowserTaskQueues::QueueType::kStartup: |
| return QueueName::UI_STARTUP_TQ; |
| } |
| } |
| |
| QueueName GetIOTaskQueueName(BrowserTaskQueues::QueueType queue_type) { |
| switch (queue_type) { |
| case BrowserTaskQueues::QueueType::kBestEffort: |
| return QueueName::IO_BEST_EFFORT_TQ; |
| case BrowserTaskQueues::QueueType::kDefault: |
| return QueueName::IO_DEFAULT_TQ; |
| case BrowserTaskQueues::QueueType::kUserBlocking: |
| return QueueName::IO_USER_BLOCKING_TQ; |
| case BrowserTaskQueues::QueueType::kUserVisible: |
| return QueueName::IO_USER_VISIBLE_TQ; |
| case BrowserTaskQueues::QueueType::kUserInput: |
| return QueueName::IO_USER_INPUT_TQ; |
| case BrowserTaskQueues::QueueType::kNavigationNetworkResponse: |
| return QueueName::IO_NAVIGATION_NETWORK_RESPONSE_TQ; |
| case BrowserTaskQueues::QueueType::kServiceWorkerStorageControlResponse: |
| return QueueName::IO_SERVICE_WORKER_STORAGE_CONTROL_RESPONSE_TQ; |
| case BrowserTaskQueues::QueueType::kBeforeUnloadBrowserResponse: |
| return QueueName::IO_BEFORE_UNLOAD_BROWSER_RESPONSE_TQ; |
| case BrowserTaskQueues::QueueType::kStartup: |
| return QueueName::IO_STARTUP_TQ; |
| } |
| } |
| |
| QueueName GetTaskQueueName(BrowserThread::ID thread_id, |
| BrowserTaskQueues::QueueType queue_type) { |
| switch (thread_id) { |
| case BrowserThread::UI: |
| return GetUITaskQueueName(queue_type); |
| case BrowserThread::IO: |
| return GetIOTaskQueueName(queue_type); |
| case BrowserThread::ID_COUNT: |
| break; |
| } |
| NOTREACHED(); |
| } |
| |
| } // namespace |
| |
| BrowserTaskQueues::QueueData::QueueData() = default; |
| BrowserTaskQueues::QueueData::~QueueData() = default; |
| BrowserTaskQueues::QueueData::QueueData(BrowserTaskQueues::QueueData&& other) = |
| default; |
| |
| BrowserTaskQueues::Handle::~Handle() = default; |
| |
| BrowserTaskQueues::Handle::Handle(BrowserTaskQueues* outer) |
| : outer_(outer), |
| control_task_runner_(outer_->control_queue_->task_runner()), |
| default_task_runner_(outer_->GetDefaultTaskQueue()->task_runner()), |
| browser_task_runners_(outer_->CreateBrowserTaskRunners()) {} |
| |
| void BrowserTaskQueues::Handle::OnStartupComplete() { |
| control_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&BrowserTaskQueues::OnStartupComplete, |
| base::Unretained(outer_))); |
| } |
| |
| void BrowserTaskQueues::Handle::EnableAllExceptBestEffortQueues() { |
| control_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&BrowserTaskQueues::EnableAllExceptBestEffortQueues, |
| base::Unretained(outer_))); |
| } |
| |
| void BrowserTaskQueues::Handle::EnableTaskQueue(QueueType type) { |
| control_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&BrowserTaskQueues::EnableTaskQueue, |
| base::Unretained(outer_), type)); |
| } |
| |
| void BrowserTaskQueues::Handle::ScheduleRunAllPendingTasksForTesting( |
| base::OnceClosure on_pending_task_ran) { |
| control_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &BrowserTaskQueues::StartRunAllPendingTasksForTesting, |
| base::Unretained(outer_), |
| base::ScopedClosureRunner(std::move(on_pending_task_ran)))); |
| } |
| |
| BrowserTaskQueues::BrowserTaskQueues( |
| BrowserThread::ID thread_id, |
| base::sequence_manager::SequenceManager* sequence_manager) { |
| for (size_t i = 0; i < queue_data_.size(); ++i) { |
| queue_data_[i].task_queue = sequence_manager->CreateTaskQueue( |
| base::sequence_manager::TaskQueue::Spec( |
| GetTaskQueueName(thread_id, static_cast<QueueType>(i)))); |
| queue_data_[i].voter = queue_data_[i].task_queue->CreateQueueEnabledVoter(); |
| queue_data_[i].voter->SetVoteToEnable(false); |
| } |
| |
| GetBrowserTaskQueue(QueueType::kUserVisible) |
| ->SetQueuePriority(BrowserTaskPriority::kLowPriority); |
| |
| // Best effort queue |
| GetBrowserTaskQueue(QueueType::kBestEffort) |
| ->SetQueuePriority(BrowserTaskPriority::kBestEffortPriority); |
| |
| // User Input queue |
| GetBrowserTaskQueue(QueueType::kUserInput) |
| ->SetQueuePriority(BrowserTaskPriority::kHighestPriority); |
| |
| GetBrowserTaskQueue(QueueType::kNavigationNetworkResponse) |
| ->SetQueuePriority(BrowserTaskPriority::kHighPriority); |
| |
| GetBrowserTaskQueue(QueueType::kServiceWorkerStorageControlResponse) |
| ->SetQueuePriority(BrowserTaskPriority::kHighestPriority); |
| |
| GetBrowserTaskQueue(QueueType::kBeforeUnloadBrowserResponse) |
| ->SetQueuePriority(BrowserTaskPriority::kHighPriority); |
| |
| // Control queue |
| control_queue_ = |
| sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec( |
| GetControlTaskQueueName(thread_id))); |
| control_queue_->SetQueuePriority(BrowserTaskPriority::kControlPriority); |
| |
| // Run all pending queue |
| run_all_pending_tasks_queue_ = |
| sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec( |
| GetRunAllPendingTaskQueueName(thread_id))); |
| run_all_pending_tasks_queue_->SetQueuePriority( |
| BrowserTaskPriority::kBestEffortPriority); |
| |
| handle_ = base::AdoptRef(new Handle(this)); |
| } |
| |
| BrowserTaskQueues::~BrowserTaskQueues() { |
| for (auto& queue : queue_data_) { |
| queue.task_queue.reset(); |
| } |
| control_queue_.reset(); |
| run_all_pending_tasks_queue_.reset(); |
| handle_->OnTaskQueuesDestroyed(); |
| } |
| |
| std::array<scoped_refptr<base::SingleThreadTaskRunner>, |
| BrowserTaskQueues::kNumQueueTypes> |
| BrowserTaskQueues::CreateBrowserTaskRunners() const { |
| std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes> |
| task_runners; |
| for (size_t i = 0; i < queue_data_.size(); ++i) { |
| task_runners[i] = queue_data_[i].task_queue->task_runner(); |
| } |
| return task_runners; |
| } |
| |
| void BrowserTaskQueues::OnStartupComplete() { |
| // Enable all queues |
| for (const auto& queue : queue_data_) { |
| queue.voter->SetVoteToEnable(true); |
| } |
| |
| // Update ServiceWorker task queue priority. |
| DCHECK_EQ( |
| static_cast<BrowserTaskPriority>( |
| GetBrowserTaskQueue(QueueType::kServiceWorkerStorageControlResponse) |
| ->GetQueuePriority()), |
| BrowserTaskPriority::kHighestPriority); |
| GetBrowserTaskQueue(QueueType::kServiceWorkerStorageControlResponse) |
| ->SetQueuePriority(BrowserTaskPriority::kHighPriority); |
| } |
| |
| void BrowserTaskQueues::EnableTaskQueue(QueueType type) { |
| queue_data_[static_cast<size_t>(type)].voter->SetVoteToEnable(true); |
| } |
| |
| void BrowserTaskQueues::EnableAllExceptBestEffortQueues() { |
| for (size_t i = 0; i < queue_data_.size(); ++i) { |
| if (i != static_cast<size_t>(QueueType::kBestEffort)) { |
| queue_data_[i].voter->SetVoteToEnable(true); |
| } |
| } |
| } |
| |
| // To run all pending tasks we do the following. We insert a fence in all queues |
| // and post a task to the |run_all_pending_queue_| which has the lowest priority |
| // possible. That makes sure that all tasks up to the fences will have run |
| // before this posted task runs. Note that among tasks with the same priority |
| // ties are broken by using the enqueue order, so all prior best effort tasks |
| // will have run before this one does. This task will then remove all the fences |
| // and call the user provided callback to signal that all pending tasks have |
| // run. This method is "reentrant" as in we can call it multiple times as the |
| // fences will just be moved back, but we need to make sure that only the last |
| // call removes the fences, for that we keep track of "nesting" with |
| // |run_all_pending_nesting_level_| |
| void BrowserTaskQueues::StartRunAllPendingTasksForTesting( |
| base::ScopedClosureRunner on_pending_task_ran) { |
| ++run_all_pending_nesting_level_; |
| for (const auto& queue : queue_data_) { |
| queue.task_queue->InsertFence(InsertFencePosition::kNow); |
| } |
| run_all_pending_tasks_queue_->task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&BrowserTaskQueues::EndRunAllPendingTasksForTesting, |
| base::Unretained(this), std::move(on_pending_task_ran))); |
| } |
| |
| void BrowserTaskQueues::EndRunAllPendingTasksForTesting( |
| base::ScopedClosureRunner on_pending_task_ran) { |
| --run_all_pending_nesting_level_; |
| if (run_all_pending_nesting_level_ == 0) { |
| for (const auto& queue : queue_data_) { |
| queue.task_queue->RemoveFence(); |
| } |
| } |
| } |
| |
| void BrowserTaskQueues::SetOnTaskCompletedHandler( |
| base::sequence_manager::TaskQueue::OnTaskCompletedHandler handler) { |
| for (auto& queue : queue_data_) { |
| queue.task_queue->SetOnTaskCompletedHandler(handler); |
| } |
| } |
| |
| void BrowserTaskQueues::AddTaskObserver(base::TaskObserver* task_observer) { |
| for (const auto& queue : queue_data_) { |
| queue.task_queue->AddTaskObserver(task_observer); |
| } |
| } |
| |
| } // namespace content |