BrowserProcessSubThread
chromium WinMain中说到,浏览器主进程Browser的工作线程一部分,其类是BrowserProcessSubThread数据结构,那么我们来分析一下这个类。这个类继承自BrowserThreadImpl,而后者继承BrowserThread和base::Thread。base::Thread继承PlatformThread::Delegate,我们慢慢来研究线程的管理过程。
对于BrowserProcessSubThread类,没有什么可以值得分析的,主要是分装了一下BrowserThreadImpl接口,主要的都在后者内部中。
windbg中chromium工作线程的观察
我们观察DB线程的执行堆栈,这个是创建后的线程执行过程,详细的说是某一时刻的运行过程。
0:020> kL
# Child-SP RetAddr Call Site
00 00000000`0684f648 000007fe`fdb910dc ntdll!NtWaitForSingleObject+0xa
01 00000000`0684f650 000007fe`ef200e54 KERNELBASE!WaitForSingleObjectEx+0x79
02 (Inline Function) --------`-------- chrome_7feef160000!base::WaitableEvent::TimedWait+0x95
03 00000000`0684f6f0 000007fe`ef1edd83 chrome_7feef160000!base::MessagePumpDefault::Run+0x1c4
04 (Inline Function) --------`-------- chrome_7feef160000!base::MessageLoop::RunHandler+0x15
05 00000000`0684f950 000007fe`ef1c4381 chrome_7feef160000!base::RunLoop::Run+0x83
06 (Inline Function) --------`-------- chrome_7feef160000!base::MessageLoop::Run+0x35
07 00000000`0684f9a0 000007fe`f01d8076 chrome_7feef160000!base::Thread::Run+0x41
08 00000000`0684fa00 000007fe`f01d8d3e chrome_7feef160000!content::BrowserThreadImpl::DBThreadRun+0x36
09 00000000`0684fb50 000007fe`ef1c46d8 chrome_7feef160000!content::BrowserThreadImpl::Run+0xca
0a 00000000`0684fb80 000007fe`ef1d7e9d chrome_7feef160000!base::Thread::ThreadMain+0x338
0b 00000000`0684fbf0 00000000`7714652d chrome_7feef160000!base::`anonymous namespace'::ThreadFunc+0x15d
0c 00000000`0684fc60 00000000`7762c521 kernel32!BaseThreadInitThunk+0xd
0d 00000000`0684fc90 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
这里面,DB线程处于等待工作中,下面我们从线程的初始化,启动,运行详细的讲解线程的生命周期。
BrowserThreadImpl
CrBrowserMain,即主进程主线程名,其如下初始化自己的主线程的:
main_thread_.reset(
new BrowserThreadImpl(BrowserThread::UI, base::MessageLoop::current()));
我们知道,这是个UI线程,线程消息循环即为当前的消息循环。
线程的初始化
BrowserThreadImpl::BrowserThreadImpl(ID identifier,
base::MessageLoop* message_loop)
: Thread(message_loop->thread_name()), identifier_(identifier) {
set_message_loop(message_loop);
Initialize();
}
void BrowserThreadImpl::Initialize() {
BrowserThreadGlobals& globals = g_globals.Get();
base::AutoLock lock(globals.lock);
DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
DCHECK(globals.threads[identifier_] == NULL);
globals.threads[identifier_] = this;
}
BrowserThreadImpl中globals管理着一批线程,至少我们知道的,Browser进程中IO,UI,DB线程都是由其管理的,so,这里就是按号入座喽。
线程的启动
bool BrowserThreadImpl::StartWithOptions(const Options& options) {
// The global thread table needs to be locked while a new thread is
// starting, as the new thread can asynchronously start touching the
// table (and other thread's message_loop).
BrowserThreadGlobals& globals = g_globals.Get();
base::AutoLock lock(globals.lock);
return Thread::StartWithOptions(options);
}
果然是一个包装,这里直接使用的是base库中的Thread启动函数。启动前加锁。
base::Thread::StartWithOptions
bool Thread::StartWithOptions(const Options& options) {
DCHECK(!message_loop_);
#if defined(OS_WIN)
DCHECK((com_status_ != STA) ||
(options.message_loop_type == MessageLoop::TYPE_UI));
#endif
// Reset |id_| here to support restarting the thread.
id_event_.Reset();
id_ = kInvalidThreadId;
SetThreadWasQuitProperly(false);
MessageLoop::Type type = options.message_loop_type;
if (!options.message_pump_factory.is_null())
type = MessageLoop::TYPE_CUSTOM;
message_loop_timer_slack_ = options.timer_slack;
scoped_ptr<MessageLoop> message_loop = MessageLoop::CreateUnbound(
type, options.message_pump_factory);
message_loop_ = message_loop.get();
start_event_.Reset();
// Hold the thread_lock_ while starting a new thread, so that we can make sure
// that thread_ is populated before the newly created thread accesses it.
{
AutoLock lock(thread_lock_);
if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_,
options.priority)) {
DLOG(ERROR) << "failed to create thread";
message_loop_ = nullptr;
return false;
}
}
// The ownership of message_loop is managemed by the newly created thread
// within the ThreadMain.
ignore_result(message_loop.release());
DCHECK(message_loop_);
return true;
}
这个函数式base thread 参数启动函数,启动前创建消息循环,使用平台类型的接口创建对应平台的线程,PlatformThread::CreateWithPriority。
base::CreateThreadInternal
// CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except
// that |out_thread_handle| may be nullptr, in which case a non-joinable thread
// is created.
bool CreateThreadInternal(size_t stack_size,
PlatformThread::Delegate* delegate,
PlatformThreadHandle* out_thread_handle,
ThreadPriority priority) {
unsigned int flags = 0;
if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) {
flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
} else {
stack_size = 0;
}
ThreadParams* params = new ThreadParams;
params->delegate = delegate;
params->joinable = out_thread_handle != nullptr;
params->priority = priority;
// Using CreateThread here vs _beginthreadex makes thread creation a bit
// faster and doesn't require the loader lock to be available. Our code will
// have to work running on CreateThread() threads anyway, since we run code
// on the Windows thread pool, etc. For some background on the difference:
// http://www.microsoft.com/msj/1099/win32/win321099.aspx
void* thread_handle =
::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr);
if (!thread_handle) {
delete params;
return false;
}
if (out_thread_handle)
*out_thread_handle = PlatformThreadHandle(thread_handle);
else
CloseHandle(thread_handle);
return true;
}
CreateThread,我们好熟悉吧,这是windows平台的线程创建接口,我们可以看到这里面传递了参数,其中一个很重要的delegate就是创建线程Thread的this指针。
base::`anonymous namespace’::ThreadFunc
DWORD __stdcall ThreadFunc(void* params) {
ThreadParams* thread_params = static_cast<ThreadParams*>(params);
PlatformThread::Delegate* delegate = thread_params->delegate;
if (!thread_params->joinable)
base::ThreadRestrictions::SetSingletonAllowed(false);
if (thread_params->priority != ThreadPriority::NORMAL)
PlatformThread::SetCurrentThreadPriority(thread_params->priority);
// Retrieve a copy of the thread handle to use as the key in the
// thread name mapping.
PlatformThreadHandle::Handle platform_handle;
BOOL did_dup = DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&platform_handle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
win::ScopedHandle scoped_platform_handle;
if (did_dup) {
scoped_platform_handle.Set(platform_handle);
ThreadIdNameManager::GetInstance()->RegisterThread(
scoped_platform_handle.Get(),
PlatformThread::CurrentId());
}
delete thread_params;
delegate->ThreadMain();
if (did_dup) {
ThreadIdNameManager::GetInstance()->RemoveName(
scoped_platform_handle.Get(),
PlatformThread::CurrentId());
}
return 0;
}
这是一个线程的入口函数,windows最先执行的就是这个函数,函数设置了线程优先级,然后执行我们的线程主函数ThreadMain,而这个函数由继承Delegate类的线程具体的实现,而我们的BrowserProcessSubThread继承自Thread,其主要来实现这个ThreadMain接口,所以线程的执行权交由Thread::ThreadMain具体来执行。
Thread::ThreadMain
void Thread::ThreadMain() {
// First, make GetThreadId() available to avoid deadlocks. It could be called
// any place in the following thread initialization code.
id_ = PlatformThread::CurrentId();
DCHECK_NE(kInvalidThreadId, id_);
id_event_.Signal();
// Complete the initialization of our Thread object.
PlatformThread::SetName(name_.c_str());
ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
// Lazily initialize the message_loop so that it can run on this thread.
DCHECK(message_loop_);
scoped_ptr<MessageLoop> message_loop(message_loop_);
message_loop_->BindToCurrentThread();
message_loop_->set_thread_name(name_);
message_loop_->SetTimerSlack(message_loop_timer_slack_);
#if defined(OS_WIN)
scoped_ptr<win::ScopedCOMInitializer> com_initializer;
if (com_status_ != NONE) {
com_initializer.reset((com_status_ == STA) ?
new win::ScopedCOMInitializer() :
new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
}
#endif
// Let the thread do extra initialization.
Init();
{
AutoLock lock(running_lock_);
running_ = true;
}
start_event_.Signal();
Run(message_loop_);
{
AutoLock lock(running_lock_);
running_ = false;
}
// Let the thread do extra cleanup.
CleanUp();
#if defined(OS_WIN)
com_initializer.reset();
#endif
if (message_loop->type() != MessageLoop::TYPE_CUSTOM) {
// Assert that MessageLoop::QuitWhenIdle was called by ThreadQuitHelper.
// Don't check for custom message pumps, because their shutdown might not
// allow this.
DCHECK(GetThreadWasQuitProperly());
}
// We can't receive messages anymore.
// (The message loop is destructed at the end of this block)
message_loop_ = nullptr;
}
函数开始处,设置平台线程名,然后设置message_loop的相关信息。然后初始化COM,之后做额外的底层子类初始化,接着调用子类的Run函数,当Run函数执行完成后,清理数据。
线程的运行
我们看开始的堆栈就明白了,这时候Browser进程的工作线程已经启动起来,该工作线程执行权已经落到了BrowserThreadImpl,一个线程已经启动成功。
因为BrowserThreadImpl管理多个工作线程,不同的线程给与不同的运行入口
void BrowserThreadImpl::Run(base::MessageLoop* message_loop) {
#if defined(OS_ANDROID)
// Not to reset thread name to "Thread-???" by VM, attach VM with thread name.
// Though it may create unnecessary VM thread objects, keeping thread name
// gives more benefit in debugging in the platform.
if (!thread_name().empty()) {
base::android::AttachCurrentThreadWithName(thread_name());
}
#endif
BrowserThread::ID thread_id = ID_COUNT;
CHECK(GetCurrentThreadIdentifier(&thread_id));
CHECK_EQ(identifier_, thread_id);
CHECK_EQ(Thread::message_loop(), message_loop);
switch (identifier_) {
case BrowserThread::UI:
return UIThreadRun(message_loop);
case BrowserThread::DB:
return DBThreadRun(message_loop);
case BrowserThread::FILE:
return FileThreadRun(message_loop);
case BrowserThread::FILE_USER_BLOCKING:
return FileUserBlockingThreadRun(message_loop);
case BrowserThread::PROCESS_LAUNCHER:
return ProcessLauncherThreadRun(message_loop);
case BrowserThread::CACHE:
return CacheThreadRun(message_loop);
case BrowserThread::IO:
return IOThreadRun(message_loop);
case BrowserThread::ID_COUNT:
CHECK(false); // This shouldn't actually be reached!
break;
}
// |identifier_| must be set to a valid enum value in the constructor, so it
// should be impossible to reach here.
CHECK(false);
}
这里作为一个分发函数,对于不同的线程执行不同的入口,我们研究的是DB线程,走的是DB线程入口。
NOINLINE void BrowserThreadImpl::DBThreadRun(base::MessageLoop* message_loop) {
volatile int line_number = __LINE__;
Thread::Run(message_loop);
CHECK_GT(line_number, 0);
}
对于DB线程来说,直接调用父类的Run函数来执行工作。
向线程投递任务
PaskTask
我们的工作线程已经初始化并且创建并且运行起来了,那么怎么向这几个Browser工作线程投递任务呢。任务的投递提供了很多的接口,用于投递不同类型的任务。
static bool PostTask(ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task);
static bool PostDelayedTask(ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay);
static bool PostNonNestableTask(ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task);
static bool PostNonNestableDelayedTask(
.......
// static
bool BrowserThread::PostTask(ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task) {
return BrowserThreadImpl::PostTaskHelper(
identifier, from_here, task, base::TimeDelta(), true);
}
对于Browser进程的工作线程来说,任务的投递有个帮助入口,这个入口主要是区分具体的投递对象,还是那句话,因为BrowserThreadImpl管理着好几个工作线程。
PostTaskHelper
// static
bool BrowserThreadImpl::PostTaskHelper(
BrowserThread::ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay,
bool nestable) {
DCHECK(identifier >= 0 && identifier < ID_COUNT);
// Optimization: to avoid unnecessary locks, we listed the ID enumeration in
// order of lifetime. So no need to lock if we know that the target thread
// outlives current thread.
// Note: since the array is so small, ok to loop instead of creating a map,
// which would require a lock because std::map isn't thread safe, defeating
// the whole purpose of this optimization.
BrowserThread::ID current_thread = ID_COUNT;
bool target_thread_outlives_current =
GetCurrentThreadIdentifier(¤t_thread) &&
current_thread >= identifier;
BrowserThreadGlobals& globals = g_globals.Get();
if (!target_thread_outlives_current)
globals.lock.Acquire();
base::MessageLoop* message_loop =
globals.threads[identifier] ? globals.threads[identifier]->message_loop()
: NULL;
if (message_loop) {
if (nestable) {
message_loop->task_runner()->PostDelayedTask(from_here, task, delay);
} else {
message_loop->task_runner()->PostNonNestableDelayedTask(from_here, task,
delay);
}
}
if (!target_thread_outlives_current)
globals.lock.Release();
return !!message_loop;
}
该函数通过ID标示获取获取线程的消息循环,然后通过线程的消息循环投递给具体的线程具体的消息循环任务。