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

原创 2012年08月24日 18:28:02
一个线程只能有一个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之间的相互关系:

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函数干的事情。


深入剖析WTL—WTL消息循环机制详解

WTL消息循环机制实现了消息过滤和空闲处理机制。 消息过滤 首先看一下CMessageLoop的核心逻辑CMessageLoop.Run()的代码: int CMessageLoop.Run()...
  • zcxin
  • zcxin
  • 2013年10月23日 00:15
  • 1185

WTL 窗口创建消息队列

ATLAPP.H包含了消息循环类、接口类、和产生应用程序所必需的一些基础类定义。        类定义如下:               CmessageFilter类---用于消息过滤的   ...

WTL框架小结

创建窗口时先注册类,通过thunk技术将窗口过程设置为StartWindowProc; 主应用程序类_Module至少有一个CMessageMap对象(可以增加、删除或者获得已经存在的引用),而CMe...

WebKit之Chromium多线程模型的设计与分析

Chromium除了远近闻名的多进程架构之外,它的多线程模型也相当引人注目的。Chromium的多进程架构是为了解决网页的稳定性问题,而多线程模型则是为了解决网页的卡顿问题。为了达到这个目的,Chro...
  • sauphy
  • sauphy
  • 2016年01月13日 00:10
  • 760

Chrome MessageLoop类分析

Windows程序是基于消息的,不管其封装形式如何,最后都要包含如下代码 MSG msg; while(GetMesssage(&msg)) { TranslateMessage(&msg...

[Chrome源码阅读] 理解ObserverList类的实现技巧

Chrome中大量用到了Observer模式,比较关键的类是ObserverList。 这个类的comment,提到了一个很关键的问题,就是在loop每个observer时,可能有observer尝...

[Chrome源码阅读] Browser相关的类

1. TabStripModel 在正式解释Brower类之前,来简要概述下TabStripModel与Browser的关系: Browser类相当于一个controller,而TabSt...

[Android源码分析]蓝牙配对之jni之上的点点滴滴

     在之前晓东已经和大家分析完成了蓝牙打开和蓝牙搜索的过程了,在搜索到设备的下一步我们要做的就是蓝牙的配对了。本文晓东将和大家一起来看看蓝牙配对究竟涉及到了哪些内容。      ...

Chromium MessageLoop类分析

原文: Windows程序是基于消息的,不管其封装形式如何,最后都要包含如下代码 Cpp代码   MSG msg;   while(GetMesssage(&msg...

[Chrome源码阅读] 理解Browser进程

首先贴出一张来自于Chrome官网上design document上的图。这张图描述了Browser进程中主要几个类之间的相互关系。 这张图仅仅列出了Browser进程里的2个线程:UI线程和I...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[Chrome源码阅读] MessageLoop类的点点滴滴
举报原因:
原因补充:

(最多只允许输入30个字)