blob: 818e5f14ee60f379c4165daa742126609b3e83de [file] [log] [blame]
// Copyright 2018 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_executor.h"
#include <atomic>
#include "base/functional/bind.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/task/deferred_sequenced_task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/threading_features.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "content/browser/browser_process_io_thread.h"
#include "content/browser/browser_thread_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/common/content_features.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/task_scheduler/post_task_android.h"
#include "base/android/task_scheduler/task_runner_android.h"
#include "base/android/task_scheduler/task_traits_android.h"
#endif
using QueueType = content::BrowserTaskQueues::QueueType;
namespace content {
namespace features {
// When the "BrowserPrioritizeInputQueue" feature is enabled, the browser will
// schedule tasks related to input in kHigh priority queue. This puts it under
// bootstrap, but above regular tasks.
//
// The goal is to reduce jank by ensuring chromium is handling input events as
// soon as possible.
//
// TODO(nuskos): Remove this feature flag after we've done our retroactive study
// of all chrometto performance improvements.
BASE_FEATURE(kBrowserPrioritizeInputQueue,
"BrowserPrioritizeInputQueue",
base::FEATURE_ENABLED_BY_DEFAULT);
} // namespace features
namespace {
// |g_browser_task_executor| is intentionally leaked on shutdown.
BrowserTaskExecutor* g_browser_task_executor = nullptr;
#if BUILDFLAG(IS_ANDROID)
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForAndroidMainThread(
::TaskTraits android_traits) {
BrowserTaskTraits traits;
switch (android_traits) {
case ::TaskTraits::UI_BEST_EFFORT:
traits = {base::TaskPriority::BEST_EFFORT};
break;
case ::TaskTraits::UI_USER_VISIBLE:
traits = {base::TaskPriority::USER_VISIBLE};
break;
case ::TaskTraits::UI_USER_BLOCKING:
traits = {base::TaskPriority::USER_BLOCKING};
break;
default:
NOTREACHED();
}
return g_browser_task_executor->GetUIThreadTaskRunner(traits);
}
#endif
} // namespace
BaseBrowserTaskExecutor::BaseBrowserTaskExecutor() = default;
BaseBrowserTaskExecutor::~BaseBrowserTaskExecutor() = default;
scoped_refptr<base::SingleThreadTaskRunner>
BaseBrowserTaskExecutor::GetTaskRunner(BrowserThread::ID identifier,
const BrowserTaskTraits& traits) const {
const QueueType queue_type = GetQueueType(traits);
switch (identifier) {
case BrowserThread::UI: {
return browser_ui_thread_handle_->GetBrowserTaskRunner(queue_type);
}
case BrowserThread::IO:
return browser_io_thread_handle_->GetBrowserTaskRunner(queue_type);
case BrowserThread::ID_COUNT:
NOTREACHED();
}
return nullptr;
}
// static
QueueType BaseBrowserTaskExecutor::GetQueueType(
const BrowserTaskTraits& traits) {
switch (traits.task_type()) {
case BrowserTaskType::kUserInput:
if (base::FeatureList::IsEnabled(
features::kBrowserPrioritizeInputQueue)) {
return QueueType::kUserInput;
}
// Defer to traits.priority() below.
break;
case BrowserTaskType::kNavigationNetworkResponse:
if (base::FeatureList::IsEnabled(
::features::kNavigationNetworkResponseQueue)) {
return QueueType::kNavigationNetworkResponse;
}
// Defer to traits.priority() below.
break;
case BrowserTaskType::kServiceWorkerStorageControlResponse:
return QueueType::kServiceWorkerStorageControlResponse;
case BrowserTaskType::kDefault:
// Defer to traits.priority() below.
break;
}
switch (traits.priority()) {
case base::TaskPriority::BEST_EFFORT:
return QueueType::kBestEffort;
case base::TaskPriority::USER_VISIBLE:
return QueueType::kUserVisible;
case base::TaskPriority::USER_BLOCKING:
return QueueType::kUserBlocking;
}
}
BrowserTaskExecutor::BrowserTaskExecutor(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate)
: ui_thread_executor_(std::make_unique<UIThreadExecutor>(
std::move(browser_ui_thread_scheduler))),
io_thread_executor_(std::make_unique<IOThreadExecutor>(
std::move(browser_io_thread_delegate))) {
browser_ui_thread_handle_ = ui_thread_executor_->GetUIThreadHandle();
browser_io_thread_handle_ = io_thread_executor_->GetIOThreadHandle();
ui_thread_executor_->SetIOThreadHandle(browser_io_thread_handle_);
io_thread_executor_->SetUIThreadHandle(browser_ui_thread_handle_);
}
BrowserTaskExecutor::~BrowserTaskExecutor() = default;
// static
void BrowserTaskExecutor::Create() {
DCHECK(!base::SingleThreadTaskRunner::HasCurrentDefault());
CreateInternal(std::make_unique<BrowserUIThreadScheduler>(),
std::make_unique<BrowserIOThreadDelegate>());
}
// static
void BrowserTaskExecutor::CreateForTesting(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate) {
CreateInternal(std::move(browser_ui_thread_scheduler),
std::move(browser_io_thread_delegate));
}
// static
void BrowserTaskExecutor::CreateInternal(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate) {
DCHECK(!g_browser_task_executor);
g_browser_task_executor =
new BrowserTaskExecutor(std::move(browser_ui_thread_scheduler),
std::move(browser_io_thread_delegate));
g_browser_task_executor->browser_ui_thread_handle_
->EnableAllExceptBestEffortQueues();
#if BUILDFLAG(IS_ANDROID)
// In Android Java, UI thread is a base/ concept, but needs to know how that
// maps onto the BrowserThread::UI in C++.
base::TaskRunnerAndroid::SetUiThreadTaskRunnerCallback(
base::BindRepeating(&GetTaskRunnerForAndroidMainThread));
base::PostTaskAndroid::SignalNativeSchedulerReady();
#endif
}
// static
BrowserTaskExecutor* BrowserTaskExecutor::Get() {
DCHECK(g_browser_task_executor)
<< "No browser task executor created.\nHint: if this is in a unit test, "
"you're likely missing a content::BrowserTaskEnvironment member in "
"your fixture.";
return g_browser_task_executor;
}
// static
void BrowserTaskExecutor::ResetForTesting() {
if (g_browser_task_executor) {
RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
delete g_browser_task_executor;
g_browser_task_executor = nullptr;
}
}
// static
void BrowserTaskExecutor::PostFeatureListSetup() {
DCHECK(Get()->ui_thread_executor_);
Get()->ui_thread_executor_->PostFeatureListSetup();
}
// static
absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
BrowserTaskExecutor::OnUserInputStart() {
DCHECK(Get()->ui_thread_executor_);
return absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>(
Get()->ui_thread_executor_->OnUserInputStart());
}
// static
void BrowserTaskExecutor::Shutdown() {
if (!g_browser_task_executor)
return;
DCHECK(Get()->ui_thread_executor_);
DCHECK(Get()->io_thread_executor_);
// We don't delete |g_browser_task_executor| because other threads may
// PostTask or call BrowserTaskExecutor::GetTaskRunner while we're tearing
// things down. We don't want to add locks so we just leak instead of dealing
// with that. For similar reasons we don't need to call
// PostTaskAndroid::SignalNativeSchedulerShutdown on Android. In tests however
// we need to clean up, so BrowserTaskExecutor::ResetForTesting should be
// called.
Get()->ui_thread_executor_.reset();
Get()->io_thread_executor_.reset();
}
// static
void BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
BrowserThread::ID identifier) {
DCHECK(Get());
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
switch (identifier) {
case BrowserThread::UI:
Get()->browser_ui_thread_handle_->ScheduleRunAllPendingTasksForTesting(
run_loop.QuitClosure());
break;
case BrowserThread::IO: {
Get()->browser_io_thread_handle_->ScheduleRunAllPendingTasksForTesting(
run_loop.QuitClosure());
break;
}
case BrowserThread::ID_COUNT:
NOTREACHED();
}
run_loop.Run();
}
// static
void BrowserTaskExecutor::OnStartupComplete() {
Get()->browser_ui_thread_handle_->OnStartupComplete();
Get()->browser_io_thread_handle_->OnStartupComplete();
}
// static
scoped_refptr<base::SingleThreadTaskRunner>
BrowserTaskExecutor::GetUIThreadTaskRunner(const BrowserTaskTraits& traits) {
return Get()->GetTaskRunner(BrowserThread::UI, traits);
}
// static
scoped_refptr<base::SingleThreadTaskRunner>
BrowserTaskExecutor::GetIOThreadTaskRunner(const BrowserTaskTraits& traits) {
return Get()->GetTaskRunner(BrowserThread::IO, traits);
}
// static
void BrowserTaskExecutor::InitializeIOThread() {
Get()->browser_io_thread_handle_->EnableAllExceptBestEffortQueues();
}
std::unique_ptr<BrowserProcessIOThread> BrowserTaskExecutor::CreateIOThread() {
DCHECK(Get()->io_thread_executor_);
std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate =
Get()->io_thread_executor_->TakeDelegate();
DCHECK(browser_io_thread_delegate);
TRACE_EVENT0("startup", "BrowserTaskExecutor::CreateIOThread");
auto io_thread = std::make_unique<BrowserProcessIOThread>();
if (browser_io_thread_delegate->allow_blocking_for_testing()) {
io_thread->AllowBlockingForTesting();
}
base::Thread::Options options;
options.message_pump_type = base::MessagePumpType::IO;
options.delegate = std::move(browser_io_thread_delegate);
// TODO(1329208): Align Win ThreadType with other platforms. The platform
// discrepancy stems from organic evolution of the thread priorities on each
// platform and while it might make sense not to bump the priority of the IO
// thread per Windows' priority boosts capabilities on MessagePumpForIO, this
// should at least be aligned with what platform_thread_win.cc does for
// ThreadType::kDisplayCritical (IO pumps in other processes) and it currently
// does not.
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(base::kAboveNormalCompositingBrowserWin)) {
options.thread_type = base::ThreadType::kCompositing;
}
#else
// Up the priority of the |io_thread_| as some of its IPCs relate to
// display tasks.
options.thread_type = base::ThreadType::kCompositing;
#endif
if (!io_thread->StartWithOptions(std::move(options)))
LOG(FATAL) << "Failed to start BrowserThread:IO";
return io_thread;
}
BrowserTaskExecutor::UIThreadExecutor::UIThreadExecutor(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler)
: browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)) {
browser_ui_thread_handle_ = browser_ui_thread_scheduler_->GetHandle();
}
BrowserTaskExecutor::UIThreadExecutor::~UIThreadExecutor() = default;
absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
BrowserTaskExecutor::UIThreadExecutor::OnUserInputStart() {
DCHECK(browser_ui_thread_scheduler_);
return browser_ui_thread_scheduler_->OnUserInputStart();
}
void BrowserTaskExecutor::UIThreadExecutor::PostFeatureListSetup() {
DCHECK(browser_ui_thread_scheduler_);
browser_ui_thread_scheduler_->PostFeatureListSetup();
}
scoped_refptr<BrowserUIThreadScheduler::Handle>
BrowserTaskExecutor::UIThreadExecutor::GetUIThreadHandle() {
return browser_ui_thread_handle_;
}
void BrowserTaskExecutor::UIThreadExecutor::SetIOThreadHandle(
scoped_refptr<BrowserUIThreadScheduler::Handle> io_thread_handle) {
browser_io_thread_handle_ = std::move(io_thread_handle);
}
BrowserTaskExecutor::IOThreadExecutor::IOThreadExecutor(
std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate)
: browser_io_thread_delegate_(std::move(browser_io_thread_delegate)) {
// |browser_io_thread_delegate_| can be null in tests.
if (!browser_io_thread_delegate_)
return;
browser_io_thread_handle_ = browser_io_thread_delegate_->GetHandle();
}
BrowserTaskExecutor::IOThreadExecutor::~IOThreadExecutor() = default;
scoped_refptr<BrowserUIThreadScheduler::Handle>
BrowserTaskExecutor::IOThreadExecutor::GetIOThreadHandle() {
return browser_io_thread_handle_;
}
void BrowserTaskExecutor::IOThreadExecutor::SetUIThreadHandle(
scoped_refptr<BrowserUIThreadScheduler::Handle> ui_thread_handle) {
browser_ui_thread_handle_ = std::move(ui_thread_handle);
}
} // namespace content