[Chrome源码阅读] MessageLoop类的点点滴滴

时间:2022-06-11 17:23:12
一个线程只能有一个MessageLoop被实例化。 在BrowserMain函数中(这个函数是browser进程的入口函数),实例化了一个局部变量MessageLoop: MessageLoop main_message_loop ( MessageLoop:: TYPE_UI ); 之后就可以直接调用的MessageLoop的静态函数current()获取那个messageloop: MessageLoop ::current ()-> Run();这其中的技术就是线程局部存储。当构造一个messageloop是,构造函数会将这个messageloop设置到这个线程的一个局部变量中,之后每次current()调用就是获取这个局部变量的指针。注意的是它只是存储一个局部变量的指针,如果对象超出作用域销毁了,那么这个TLS中存储的就是一个野指针。可幸的是chrome为我们做足了功夫,在messageloop销毁时,析构函数就会重置这个TLS。
下面来看MessageLoop与MessagePump之间的相互关系:[Chrome源码阅读] MessageLoop类的点点滴滴
MessageLoop实现了接口MessagePump::Delegate,实现了3个处理不同类型TASK的虚函数。同时MessagePump也是一个接口类,跟平台相关的类MessagePumpWin并没有实现它的接口,而是2个特别的子类进行了实现。MessageLoop类是对外的接口,客户端代码一般不接触MessagePump。当实例化一个MessageLoop时就会实例化一个MessagePump,在Windows平台上是MessagePumpWin,针对不同的事务处理类型,会有三个不同的继承类:MessagePumpForUI,MessagePumpForIO,和MessagePumpDefault。MessagePump*类是真正干活的,MessageLoop::Run函数转调用pump_::Run函数(MessagePump),并将自己作为MessagePump::Delegate传进去。MessagePump::Run函数处理流程会按照如下的方式进行:
 for (;;) {
bool did_work = DoInternalWork();
if (should_quit_)
break;

did_work |= delegate_->DoWork();
if (should_quit_)
break;

did_work |= delegate_->DoDelayedWork();
if (should_quit_)
break;

if (did_work)
continue;

did_work = delegate_->DoIdleWork();
if (should_quit_)
break;

if (did_work)
continue;

WaitForWork();
}
从以上的流程可知,我们总是优先处理完NON-IDEL TASK,实在没有NON-IDEL TASK了,才处理IDEL TASK。不同类型的MessagePump会有不同的处理,例如Default类型只处理Task和Timer,那么它就没有UI类型需要处理Windows的消息事件,譬如下面的代码段:(MessagePumpDefault类定义在文件\src\base\message_pump_default.h,其它2个类定义在\src\base\message_pump_win.h)。
void MessagePumpDefault::Run(Delegate* delegate) {
DCHECK(keep_running_) << "Quit must have been called outside of Run!";

for (;;) {
ScopedNSAutoreleasePool autorelease_pool;

bool did_work = delegate->DoWork();
if (!keep_running_)
break;

did_work |= delegate->DoDelayedWork(&delayed_work_time_);
if (!keep_running_)
break;

if (did_work)
continue;

did_work = delegate->DoIdleWork();
if (!keep_running_)
break;

if (did_work)
continue;

if (delayed_work_time_.is_null()) {
event_.Wait();
} else {
TimeDelta delay = delayed_work_time_ - Time::Now();
if (delay > TimeDelta()) {
event_.TimedWait(delay);
} else {
// It looks like delayed_work_time_ indicates a time in the past, so we
// need to call DoDelayedWork now.
delayed_work_time_ = Time();
}
}
// Since event_ is auto-reset, we don't need to do anything special here
// other than service each delegate method.
}

keep_running_ = true;
}
我们前篇文章讲到,MessageLoop是在新的线程的栈上构造出来的,所以它的Run函数进而MessagePump::Run函数都是运行在那个新开辟的线程中的。

前面提到MessageLoop是对外的接口类,主要是因为它提供了PostTask族接口函数,这类函数方便客户端将不同类型的TASK放到当前或者其它线程的消息队列里。说是当前,是因为你可以这样调用:MessageLoop::current()->PostTask(...),MessageLoop::current()函数返回当前线程的message loop。如果想要另外一个线程帮忙执行某个TASK,而且也知道那个线程,那么可以这样调用SomeThread->message_loop()->PostTask(...)。在UI线程里通过全局变量g_browser_process可以方便的拿到其它“有名”线程地址,然后用前面的方式请求执行某一个TASK。
4个PostTask函数接口如下:
  void PostTask(
const tracked_objects::Location& from_here, Task* task);

void PostDelayedTask(
const tracked_objects::Location& from_here, Task* task, int delay_ms);

void PostNonNestableTask(
const tracked_objects::Location& from_here, Task* task);

void PostNonNestableDelayedTask(
const tracked_objects::Location& from_here, Task* task, int delay_ms);
New出来一个TASK,这个TASK的生命期就有MessageLoop来管理,在运行完那个TASK之后,就会被销毁。4个函数都会统一转调用一个内部的函数:PostTask_Helper。我们来看下这个PostTask_Help函数干了什么活:
void MessageLoop::PostTask_Helper(
const tracked_objects::Location& from_here, Task* task, int delay_ms,
bool nestable) {
task->SetBirthPlace(from_here);

PendingTask pending_task(task, nestable);

if (delay_ms > 0) {
pending_task.delayed_run_time =
Time::Now() + TimeDelta::FromMilliseconds(delay_ms);
} else {
DCHECK(delay_ms == 0) << "delay should not be negative";
}

// Warning: Don't try to short-circuit, and handle this thread's tasks more
// directly, as it could starve handling of foreign threads. Put every task
// into this queue.

scoped_refptr<base::MessagePump> pump;
{
AutoLock locked(incoming_queue_lock_);

bool was_empty = incoming_queue_.empty();
incoming_queue_.push(pending_task);
if (!was_empty)
return; // Someone else should have started the sub-pump.

pump = pump_;
}
// Since the incoming_queue_ may contain a task that destroys this message
// loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
// We use a stack-based reference to the message pump so that we can call
// ScheduleWork outside of incoming_queue_lock_.

pump->ScheduleWork();
}
可以看到它首先实例化一个PendingTask(PendingTask仅仅是对Task*进行了简单的封装,并且额外添加了一些控制参数),然后把它Enqueue到incoming_queue_队列中,注意这里enqueue时用到了lock(这么久了,好像很少用到锁,这里是一例)。如果以前队列已经有TASK,直接返回(不需要发信号给线程,注意PostTask函数是在调用线程中执行的),否则需要调用ScheduleWork来唤醒那个线程(因为没有TASK,线程就阻塞在那个信号量上)。
从COMMENT知道,代码将pump_赋值给另一个变量,用来增加引用计数,让lock可以快速释放,从而避免了一种CASE?注意这里是将PendingTask放到incomming_queue_队列中,其实MessageLoop还以另外一个TASK队列叫做work_queue_,所以说incomming_queue_只是一个缓存队列,当work_queue_为空时,才会将两者进行交换(有点类似于OPENGL的双显示缓存机制),交换前需要锁住incomming_queue_队列。这是MessageLoop::ReloadWorkQueue函数干的事情。