#include "content/browser/browser_thread_impl.h"
#include <string>
#include <utility>
#include "base/atomicops.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/sequence_checker.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/scheduler/browser_task_executor.h"
#include "content/public/browser/content_browser_client.h"
namespace content {
namespace {
enum BrowserThreadState {
UNINITIALIZED = 0,
RUNNING,
SHUTDOWN
};
struct BrowserThreadGlobals {
BrowserThreadGlobals() {
DETACH_FROM_THREAD(main_thread_checker_);
}
THREAD_CHECKER(main_thread_checker_);
scoped_refptr<base::SingleThreadTaskRunner>
task_runners[BrowserThread::ID_COUNT];
base::subtle::Atomic32 states[BrowserThread::ID_COUNT] = {};
};
BrowserThreadGlobals& GetBrowserThreadGlobals() {
static base::NoDestructor<BrowserThreadGlobals> globals;
return *globals;
}
}
scoped_refptr<base::SingleThreadTaskRunner> GetUIThreadTaskRunner(
const BrowserTaskTraits& traits) {
return BrowserTaskExecutor::GetUIThreadTaskRunner(traits);
}
scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner(
const BrowserTaskTraits& traits) {
return BrowserTaskExecutor::GetIOThreadTaskRunner(traits);
}
BrowserThreadImpl::BrowserThreadImpl(
ID identifier,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: identifier_(identifier) {
DCHECK_GE(identifier_, 0);
DCHECK_LT(identifier_, ID_COUNT);
DCHECK(task_runner);
BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier_]),
BrowserThreadState::UNINITIALIZED);
base::subtle::NoBarrier_Store(&globals.states[identifier_],
BrowserThreadState::RUNNING);
DCHECK(!globals.task_runners[identifier_]);
globals.task_runners[identifier_] = std::move(task_runner);
if (identifier_ == BrowserThread::ID::UI) {
#if BUILDFLAG(IS_POSIX)
if (!base::CurrentIOThread::IsSet()) {
file_descriptor_watcher_.emplace(GetIOThreadTaskRunner({}));
}
base::FileDescriptorWatcher::AssertAllowed();
#endif
}
}
BrowserThreadImpl::~BrowserThreadImpl() {
BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier_]),
BrowserThreadState::RUNNING);
base::subtle::NoBarrier_Store(&globals.states[identifier_],
BrowserThreadState::SHUTDOWN);
DCHECK(globals.task_runners[identifier_]);
}
void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) {
BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier]),
BrowserThreadState::SHUTDOWN);
base::subtle::NoBarrier_Store(&globals.states[identifier],
BrowserThreadState::UNINITIALIZED);
globals.task_runners[identifier] = nullptr;
}
const char* BrowserThreadImpl::GetThreadName(BrowserThread::ID thread) {
static const char* const kBrowserThreadNames[BrowserThread::ID_COUNT] = {
"",
"Chrome_IOThread",
};
if (BrowserThread::UI < thread && thread < BrowserThread::ID_COUNT)
return kBrowserThreadNames[thread];
if (thread == BrowserThread::UI)
return "Chrome_UIThread";
return "Unknown Thread";
}
bool BrowserThread::IsThreadInitialized(ID identifier) {
DCHECK_GE(identifier, 0);
DCHECK_LT(identifier, ID_COUNT);
BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
return base::subtle::NoBarrier_Load(&globals.states[identifier]) ==
BrowserThreadState::RUNNING;
}
bool BrowserThread::CurrentlyOn(ID identifier) {
DCHECK_GE(identifier, 0);
DCHECK_LT(identifier, ID_COUNT);
BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
return globals.task_runners[identifier] &&
globals.task_runners[identifier]->RunsTasksInCurrentSequence();
}
std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
std::string actual_name = base::PlatformThread::GetName();
if (actual_name.empty())
actual_name = "Unknown Thread";
std::string result = "Must be called on ";
result += BrowserThreadImpl::GetThreadName(expected);
result += "; actually called on ";
result += actual_name;
result += ".";
return result;
}
bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
for (int i = 0; i < ID_COUNT; ++i) {
if (globals.task_runners[i] &&
globals.task_runners[i]->RunsTasksInCurrentSequence()) {
*identifier = static_cast<ID>(i);
return true;
}
}
return false;
}
scoped_refptr<base::SingleThreadTaskRunner>
BrowserThread::GetTaskRunnerForThread(ID identifier) {
DCHECK_GE(identifier, 0);
DCHECK_LT(identifier, ID_COUNT);
switch (identifier) {
case UI:
return GetUIThreadTaskRunner({});
case IO:
return GetIOThreadTaskRunner({});
case ID_COUNT:
NOTREACHED();
return nullptr;
}
}
void BrowserThread::RunAllPendingTasksOnThreadForTesting(ID identifier) {
BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(identifier);
}
void BrowserThread::PostBestEffortTask(
const base::Location& from_here,
scoped_refptr<base::TaskRunner> task_runner,
base::OnceClosure task) {
content::GetIOThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
->PostTask(
FROM_HERE,
base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
std::move(task_runner), from_here, std::move(task)));
}
}