blob: b830e38d0a841d1ab9719c63716b53ab7f2b62b7 [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/check_deref.h"
#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/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/common/features.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.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 {
// |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;
case ::TaskTraits::UI_STARTUP:
traits = {BrowserTaskType::kStartup};
break;
default:
NOTREACHED();
}
return g_browser_task_executor->GetUIThreadTaskRunner(traits);
}
#endif
} // namespace
scoped_refptr<base::SingleThreadTaskRunner> BrowserTaskExecutor::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();
}
}
// static
QueueType BrowserTaskExecutor::GetQueueType(const BrowserTaskTraits& traits) {
switch (traits.task_type()) {
case BrowserTaskType::kUserInput:
return QueueType::kUserInput;
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::kBeforeUnloadBrowserResponse:
if (base::FeatureList::IsEnabled(
features::kBeforeUnloadBrowserResponseQueue)) {
return QueueType::kBeforeUnloadBrowserResponse;
}
break;
case BrowserTaskType::kStartup:
return QueueType::kStartup;
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)
: browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)),
browser_ui_thread_handle_(browser_ui_thread_scheduler_->GetHandle()),
browser_io_thread_delegate_(std::move(browser_io_thread_delegate)),
browser_io_thread_handle_(browser_io_thread_delegate_->GetHandle()) {}
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));
// Queues are disabled by default and only enabled by the BrowserTaskExecutor
// and so no task can be posted until after this point. This allows an
// embedder to control when to enable the UI task queues. This state is
// required for WebView's async startup to work properly.
g_browser_task_executor->browser_io_thread_handle_->EnableTaskQueue(
QueueType::kDefault);
g_browser_task_executor->browser_ui_thread_handle_->EnableTaskQueue(
QueueType::kStartup);
base::OnceClosure enable_native_ui_task_execution_callback =
base::BindOnce([] {
g_browser_task_executor->browser_ui_thread_handle_
->EnableAllExceptBestEffortQueues();
});
// Most tests don't have ContentClient set before BrowserTaskExecutor is
// created, so call the callback directly.
if (GetContentClient() && GetContentClient()->browser()) {
GetContentClient()->browser()->OnUiTaskRunnerReady(
std::move(enable_native_ui_task_execution_callback));
} else {
std::move(enable_native_ui_task_execution_callback).Run();
}
#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;
#if BUILDFLAG(IS_ANDROID)
base::PostTaskAndroid::ResetTaskRunnerForTesting();
#endif
}
}
// static
void BrowserTaskExecutor::Shutdown() {
if (!g_browser_task_executor)
return;
DCHECK(Get()->browser_ui_thread_scheduler_);
// 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()->browser_ui_thread_scheduler_.reset();
Get()->browser_io_thread_delegate_.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()->browser_io_thread_delegate_);
TRACE_EVENT0("startup", "BrowserTaskExecutor::CreateIOThread");
auto io_thread = std::make_unique<BrowserProcessIOThread>();
if (Get()->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(Get()->browser_io_thread_delegate_);
// Up the priority of the |io_thread_| as some of its IPCs relate to
// display tasks.
options.thread_type = base::ThreadType::kDisplayCritical;
if (!io_thread->StartWithOptions(std::move(options)))
LOG(FATAL) << "Failed to start BrowserThread:IO";
return io_thread;
}
// static
void BrowserTaskExecutor::
InstallPartitionAllocSchedulerLoopQuarantineTaskObserver() {
CHECK_DEREF(Get()->browser_ui_thread_scheduler_.get())
.InstallPartitionAllocSchedulerLoopQuarantineTaskObserver();
}
} // namespace content