BrowserProcessSubThread
来源:互联网 发布:惊悚电影推荐知乎 编辑:程序博客网 时间:2024/06/14 12:19
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 Site00 00000000`0684f648 000007fe`fdb910dc ntdll!NtWaitForSingleObject+0xa01 00000000`0684f650 000007fe`ef200e54 KERNELBASE!WaitForSingleObjectEx+0x7902 (Inline Function) --------`-------- chrome_7feef160000!base::WaitableEvent::TimedWait+0x9503 00000000`0684f6f0 000007fe`ef1edd83 chrome_7feef160000!base::MessagePumpDefault::Run+0x1c404 (Inline Function) --------`-------- chrome_7feef160000!base::MessageLoop::RunHandler+0x1505 00000000`0684f950 000007fe`ef1c4381 chrome_7feef160000!base::RunLoop::Run+0x8306 (Inline Function) --------`-------- chrome_7feef160000!base::MessageLoop::Run+0x3507 00000000`0684f9a0 000007fe`f01d8076 chrome_7feef160000!base::Thread::Run+0x4108 00000000`0684fa00 000007fe`f01d8d3e chrome_7feef160000!content::BrowserThreadImpl::DBThreadRun+0x3609 00000000`0684fb50 000007fe`ef1c46d8 chrome_7feef160000!content::BrowserThreadImpl::Run+0xca0a 00000000`0684fb80 000007fe`ef1d7e9d chrome_7feef160000!base::Thread::ThreadMain+0x3380b 00000000`0684fbf0 00000000`7714652d chrome_7feef160000!base::`anonymous namespace'::ThreadFunc+0x15d0c 00000000`0684fc60 00000000`7762c521 kernel32!BaseThreadInitThunk+0xd0d 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( ....... // staticbool 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
// staticbool 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标示获取获取线程的消息循环,然后通过线程的消息循环投递给具体的线程具体的消息循环任务。