blob: 54cae8574a500df18e74235e2fef0d60a81007fc [file] [log] [blame]
// Copyright 2019 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.
#include "content/browser/scheduler/browser_ui_thread_scheduler.h"
#include <utility>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/sequence_manager_impl.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/sequence_manager/time_domain.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "content/public/common/content_features.h"
namespace content {
BrowserUIThreadScheduler::~BrowserUIThreadScheduler() = default;
// static
std::unique_ptr<BrowserUIThreadScheduler>
BrowserUIThreadScheduler::CreateForTesting(
base::sequence_manager::SequenceManager* sequence_manager,
base::sequence_manager::TimeDomain* time_domain) {
return base::WrapUnique(
new BrowserUIThreadScheduler(sequence_manager, time_domain));
}
void BrowserUIThreadScheduler::PostFeatureListSetup() {
if (base::FeatureList::IsEnabled(features::kPrioritizeBootstrapTasks)) {
task_queues_[QueueType::kBootstrap]->SetQueuePriority(
base::sequence_manager::TaskQueue::kHighestPriority);
// Navigation and preconnection tasks are also important during startup so
// prioritize them too.
task_queues_[QueueType::kNavigationAndPreconnection]->SetQueuePriority(
base::sequence_manager::TaskQueue::kHighPriority);
}
}
void BrowserUIThreadScheduler::Shutdown() {
task_queues_.clear();
owned_sequence_manager_.reset();
sequence_manager_ = nullptr;
}
BrowserUIThreadScheduler::BrowserUIThreadScheduler()
: owned_sequence_manager_(
base::sequence_manager::CreateUnboundSequenceManager(
base::sequence_manager::SequenceManager::Settings{
.message_loop_type = base::MessageLoop::TYPE_UI})),
sequence_manager_(owned_sequence_manager_.get()),
time_domain_(sequence_manager_->GetRealTimeDomain()) {
InitialiseTaskQueues();
sequence_manager_->SetDefaultTaskRunner(GetTaskRunner(QueueType::kDefault));
sequence_manager_->BindToMessagePump(
base::MessageLoop::CreateMessagePumpForType(base::MessageLoop::TYPE_UI));
}
BrowserUIThreadScheduler::BrowserUIThreadScheduler(
base::sequence_manager::SequenceManager* sequence_manager,
base::sequence_manager::TimeDomain* time_domain)
: sequence_manager_(sequence_manager), time_domain_(time_domain) {
InitialiseTaskQueues();
}
void BrowserUIThreadScheduler::InitialiseTaskQueues() {
DCHECK(sequence_manager_);
sequence_manager_->EnableCrashKeys("ui_scheduler_async_stack");
// To avoid locks in BrowserUIThreadScheduler::GetTaskRunner, eagerly
// create all the well known task queues.
CreateTaskQueuesAndRunners();
InitialiseBestEffortQueue();
}
void BrowserUIThreadScheduler::CreateTaskQueuesAndRunners() {
for (int i = 0;
i < static_cast<int>(BrowserUIThreadTaskQueue::QueueType::kCount); i++) {
BrowserUIThreadTaskQueue::QueueType queue_type =
static_cast<BrowserUIThreadTaskQueue::QueueType>(i);
scoped_refptr<BrowserUIThreadTaskQueue> task_queue =
sequence_manager_->CreateTaskQueueWithType<BrowserUIThreadTaskQueue>(
base::sequence_manager::TaskQueue::Spec(
BrowserUIThreadTaskQueue::NameForQueueType(queue_type))
.SetTimeDomain(time_domain_),
queue_type);
task_queues_.emplace(queue_type, task_queue);
task_runners_.emplace(queue_type, task_queue->task_runner());
}
}
void BrowserUIThreadScheduler::InitialiseBestEffortQueue() {
auto queue = task_queues_[BrowserUIThreadTaskQueue::QueueType::kBestEffort];
queue->SetQueuePriority(
base::sequence_manager::TaskQueue::kBestEffortPriority);
best_effort_voter_ = queue->CreateQueueEnabledVoter();
best_effort_voter_->SetVoteToEnable(false);
}
scoped_refptr<base::SingleThreadTaskRunner>
BrowserUIThreadScheduler::GetTaskRunnerForTesting(QueueType queue_type) {
return GetTaskRunner(queue_type);
}
scoped_refptr<base::SingleThreadTaskRunner>
BrowserUIThreadScheduler::GetTaskRunner(QueueType queue_type) {
auto it = task_runners_.find(queue_type);
if (it != task_runners_.end())
return it->second;
NOTREACHED();
return scoped_refptr<base::SingleThreadTaskRunner>();
}
void BrowserUIThreadScheduler::EnableBestEffortQueues() {
DCHECK(!best_effort_voter_->IsVotingToEnable());
best_effort_voter_->SetVoteToEnable(true);
}
void BrowserUIThreadScheduler::RunAllPendingTasksForTesting() {
std::vector<scoped_refptr<BrowserUIThreadTaskQueue>> fenced_queues;
for (const auto& queue : task_queues_) {
bool had_fence = queue.second->HasActiveFence();
queue.second->InsertFence(
base::sequence_manager::TaskQueue::InsertFencePosition::kNow);
// If there was a fence already this must be a re-entrant call to this
// method. The previous statement just moved the fence further back. In this
// case we do not remove the fence as the parent run loop needs all queues
// to be fenced to be able to exit the run loop (i.e. become idle)
if (!had_fence)
fenced_queues.push_back(queue.second);
}
base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
for (const auto& queue : fenced_queues) {
queue->RemoveFence();
}
}
} // namespace content