blob: 353d009a47103208eba524d0d05b8a569e1ede7e [file] [log] [blame]
// Copyright 2018 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_task_executor.h"
#include <atomic>
#include "base/bind.h"
#include "base/deferred_sequenced_task_runner.h"
#include "base/no_destructor.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/browser_process_sub_thread.h"
#include "content/browser/browser_thread_impl.h"
#include "content/public/browser/browser_task_traits.h"
#if defined(OS_ANDROID)
#include "base/android/task_scheduler/post_task_android.h"
#endif
namespace content {
namespace {
using QueueType = ::content::BrowserTaskQueues::QueueType;
// |g_browser_task_executor| is intentionally leaked on shutdown.
BrowserTaskExecutor* g_browser_task_executor = nullptr;
QueueType GetQueueType(const base::TaskTraits& traits,
BrowserTaskType task_type) {
switch (task_type) {
case BrowserTaskType::kBootstrap:
// Note we currently ignore the priority for bootstrap tasks.
return QueueType::kBootstrap;
case BrowserTaskType::kNavigation:
case BrowserTaskType::kPreconnect:
// Note we currently ignore the priority for navigation and preconnection
// tasks.
return QueueType::kNavigationAndPreconnection;
case BrowserTaskType::kDefault:
// Defer to traits.priority() below.
break;
case BrowserTaskType::kBrowserTaskType_Last:
NOTREACHED();
}
switch (traits.priority()) {
case base::TaskPriority::BEST_EFFORT:
return QueueType::kBestEffort;
case base::TaskPriority::USER_VISIBLE:
return QueueType::kDefault;
case base::TaskPriority::USER_BLOCKING:
return QueueType::kUserBlocking;
}
}
} // namespace
BrowserTaskExecutor::BrowserTaskExecutor(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
std::unique_ptr<BrowserIOTaskEnvironment> browser_io_task_environment)
: browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)),
browser_ui_thread_handle_(browser_ui_thread_scheduler_->GetHandle()),
browser_io_task_environment_(std::move(browser_io_task_environment)),
browser_io_thread_handle_(browser_io_task_environment_->CreateHandle()) {}
BrowserTaskExecutor::~BrowserTaskExecutor() = default;
// static
void BrowserTaskExecutor::Create() {
DCHECK(!base::ThreadTaskRunnerHandle::IsSet());
CreateInternal(std::make_unique<BrowserUIThreadScheduler>(),
std::make_unique<BrowserIOTaskEnvironment>());
}
// static
void BrowserTaskExecutor::CreateForTesting(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
std::unique_ptr<BrowserIOTaskEnvironment> browser_io_task_environment) {
CreateInternal(std::move(browser_ui_thread_scheduler),
std::move(browser_io_task_environment));
}
// static
void BrowserTaskExecutor::CreateInternal(
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler,
std::unique_ptr<BrowserIOTaskEnvironment> browser_io_task_environment) {
DCHECK(!g_browser_task_executor);
g_browser_task_executor =
new BrowserTaskExecutor(std::move(browser_ui_thread_scheduler),
std::move(browser_io_task_environment));
base::RegisterTaskExecutor(BrowserTaskTraitsExtension::kExtensionId,
g_browser_task_executor);
g_browser_task_executor->browser_ui_thread_handle_
.EnableAllExceptBestEffortQueues();
#if defined(OS_ANDROID)
base::PostTaskAndroid::SignalNativeSchedulerReady();
#endif
}
// static
void BrowserTaskExecutor::ResetForTesting() {
#if defined(OS_ANDROID)
base::PostTaskAndroid::SignalNativeSchedulerShutdown();
#endif
if (g_browser_task_executor) {
base::UnregisterTaskExecutorForTesting(
BrowserTaskTraitsExtension::kExtensionId);
delete g_browser_task_executor;
g_browser_task_executor = nullptr;
}
}
// static
void BrowserTaskExecutor::PostFeatureListSetup() {
DCHECK(g_browser_task_executor);
DCHECK(g_browser_task_executor->browser_ui_thread_scheduler_);
DCHECK(g_browser_task_executor->browser_io_task_environment_);
g_browser_task_executor->browser_ui_thread_handle_
.PostFeatureListInitializationSetup();
g_browser_task_executor->browser_io_thread_handle_
.PostFeatureListInitializationSetup();
}
// static
void BrowserTaskExecutor::Shutdown() {
if (!g_browser_task_executor)
return;
DCHECK(g_browser_task_executor->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.
g_browser_task_executor->browser_ui_thread_scheduler_.reset();
g_browser_task_executor->browser_io_task_environment_.reset();
}
// static
void BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
BrowserThread::ID identifier) {
DCHECK(g_browser_task_executor);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
switch (identifier) {
case BrowserThread::UI:
g_browser_task_executor->browser_ui_thread_handle_
.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
break;
case BrowserThread::IO: {
g_browser_task_executor->browser_io_thread_handle_
.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
break;
}
case BrowserThread::ID_COUNT:
NOTREACHED();
}
run_loop.Run();
}
bool BrowserTaskExecutor::PostDelayedTask(const base::Location& from_here,
const base::TaskTraits& traits,
base::OnceClosure task,
base::TimeDelta delay) {
DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
const BrowserTaskTraitsExtension& extension =
traits.GetExtension<BrowserTaskTraitsExtension>();
if (extension.nestable()) {
return GetTaskRunner(traits)->PostDelayedTask(from_here, std::move(task),
delay);
} else {
return GetTaskRunner(traits)->PostNonNestableDelayedTask(
from_here, std::move(task), delay);
}
}
scoped_refptr<base::TaskRunner> BrowserTaskExecutor::CreateTaskRunner(
const base::TaskTraits& traits) {
return GetTaskRunner(traits);
}
scoped_refptr<base::SequencedTaskRunner>
BrowserTaskExecutor::CreateSequencedTaskRunner(const base::TaskTraits& traits) {
return GetTaskRunner(traits);
}
scoped_refptr<base::SingleThreadTaskRunner>
BrowserTaskExecutor::CreateSingleThreadTaskRunner(
const base::TaskTraits& traits,
base::SingleThreadTaskRunnerThreadMode thread_mode) {
return GetTaskRunner(traits);
}
#if defined(OS_WIN)
scoped_refptr<base::SingleThreadTaskRunner>
BrowserTaskExecutor::CreateCOMSTATaskRunner(
const base::TaskTraits& traits,
base::SingleThreadTaskRunnerThreadMode thread_mode) {
return GetTaskRunner(traits);
}
#endif // defined(OS_WIN)
scoped_refptr<base::SingleThreadTaskRunner> BrowserTaskExecutor::GetTaskRunner(
const base::TaskTraits& traits) const {
auto id_and_queue = GetThreadIdAndQueueType(traits);
switch (id_and_queue.thread_id) {
case BrowserThread::UI: {
return browser_ui_thread_handle_.GetBrowserTaskRunner(
id_and_queue.queue_type);
}
case BrowserThread::IO:
return browser_io_thread_handle_.GetBrowserTaskRunner(
id_and_queue.queue_type);
case BrowserThread::ID_COUNT:
NOTREACHED();
}
return nullptr;
}
// static
BrowserTaskExecutor::ThreadIdAndQueueType
BrowserTaskExecutor::GetThreadIdAndQueueType(const base::TaskTraits& traits) {
DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
BrowserTaskTraitsExtension extension =
traits.GetExtension<BrowserTaskTraitsExtension>();
BrowserThread::ID thread_id = extension.browser_thread();
DCHECK_GE(thread_id, 0);
BrowserTaskType task_type = extension.task_type();
DCHECK_LT(task_type, BrowserTaskType::kBrowserTaskType_Last);
return {thread_id, GetQueueType(traits, task_type)};
}
// static
void BrowserTaskExecutor::EnableAllQueues() {
DCHECK(g_browser_task_executor);
g_browser_task_executor->browser_ui_thread_handle_.EnableAllQueues();
g_browser_task_executor->browser_io_thread_handle_.EnableAllQueues();
}
// static
void BrowserTaskExecutor::InitializeIOThread() {
DCHECK(g_browser_task_executor);
g_browser_task_executor->browser_io_thread_handle_
.EnableAllExceptBestEffortQueues();
}
std::unique_ptr<BrowserProcessSubThread> BrowserTaskExecutor::CreateIOThread() {
DCHECK(g_browser_task_executor);
DCHECK(g_browser_task_executor->browser_io_task_environment_);
TRACE_EVENT0("startup", "BrowserTaskExecutor::CreateIOThread");
auto io_thread = std::make_unique<BrowserProcessSubThread>(BrowserThread::IO);
if (g_browser_task_executor->browser_io_task_environment_
->allow_blocking_for_testing()) {
io_thread->AllowBlockingForTesting();
}
base::Thread::Options options;
options.message_loop_type = base::MessagePump::Type::IO;
options.task_environment =
g_browser_task_executor->browser_io_task_environment_.release();
#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(USE_OZONE)
// Up the priority of the |io_thread_| as some of its IPCs relate to
// display tasks.
options.priority = base::ThreadPriority::DISPLAY;
#endif
if (!io_thread->StartWithOptions(options))
LOG(FATAL) << "Failed to start BrowserThread:IO";
return io_thread;
}
} // namespace content