Chromium多线程模型设计和实现分析

       Chromium除了远近闻名的多进程架构之外,它的多线程模型也相当引人注目的。Chromium的多进程架构是为了解决网页的稳定性问题,而多线程模型则是为了解决网页的卡顿问题。为了达到这个目的,Chromium的多线程模型是基于异步通信的。也就是说,一个线程请求另外一个线程执行一个任务的时候,不需要等待该任务完成就可以去做其它事情,从而避免了卡顿。本文就分析Chromium的多线程模型的设计和实现。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

       有同学看到这里可能会有疑问,如果一个线程请求另外一个线程执行一个任务的时候,就是要等该任务完成之后才能做其它事情,那么使用异步通信有什么用呢?的确如此,但是Chromium提供这种基于异步通信的多线程模型,就是希望作为开发者的你在实现一个模块的时候,尽最大努力地设计好各个子模块及其对应的数据结构,使得它们在协作时可以最大程度地进行异步通信。因此,Chromium基于异步通信的多线程模型更多的是体现一种设计哲学。

       一个典型的异步通信过程如图1所示:


图1  线程异步通信过程

       Task-1被分解成三个子任务Task-1(1)、Task-1(2)和Task-1(3)。其中,Task-1(1)由Thread-1执行。Task-1(1)执行完成后,Thread-1通过我们在前面Chromium多线程通信的Closure机制分析一文分析的Closure请求Thread-2执行Task-1(2)。Task-1(2)执行完成后,Thread-2又通过一个Closure请求Thread-1执行Task-1(3)。至此,Task-1就执行完成。我们可以将第一个Closure看作是一个Request操作,而第二个Closure是一个Reply操作。这是一个典型的异步通信过程。当然,如果不需要知道异步通信结果,那么第二个Closure和Task-1(3)就是不需要的。

       假设Thread-1需要知道异步通信的结果,那么在图1中我们可以看到一个非常关键的点:Thread-1并不是什么也不干就只是等着Thread-2执行完成Task-1(2),它趁着这个等待的空隙,干了另外一件事情——Task-2。如果我们将Thread-1看作是一个UI线程,那么就意味着这种异步通信模式是可以提高它的响应性的。

       为了能够完成上述的异步通信过程,一个线程的生命周期如图2所示:


图2 线程生命周期

       线程经过短暂的启动之后(Start),就围绕着一个任务队列(TaskQueue)不断地进行循环,直到被通知停止为止(Stop)。在围绕任务队列循环期间,它会不断地检查任务队列是否为空。如果不为空,那么就会将里面的任务(Task)取出来,并且进行处理。这样,一个线程如果要请求另外一个线程执行某一个操作,那么只需要将该操作封装成一个任务,并且发送到目标线程的任务队列去即可。

       为了更好地理解这种基于任务队列的线程运行模式,我们脑补一下另外一种常用的基于锁的线程运行模式。一个线程要执行某一个操作的时候,就直接调用一个代表该操作的一个函数。如果该函数需要访问全局数据或者共享数据,那么就需要进行加锁,避免其它线程也正在访问这些全局数据或者共享数据。这样做的一个好处是我们只需要关心问题的建模,而不需要关心问题是由谁来执行的,只要保证逻辑正确并且数据完整即可。当然坏处也是显然的。首先是为了保持数据完整性,也就是避免访问数据时出现竞争条件,代码里面充斥着各种锁。其次,如果多个线程同时获取同一个锁,那么就会产生竞争。这种锁竞争会带来额外的开销,从而降低线程的响应性。

       基于任务队列的线程运行模式,要求在对问题进行建模时,要提前知道谁是执行者。也就是说,在对问题进行建模时,需要指派好每一个子问题的执行者。这样我们为子问题设计数据结构时,就规定这些数据结构仅仅会被子问题的执行者访问。这样执行者在解决指派给它的问题时,就不需要进行加锁操作,因为在解决问题过程中需要访问的数据不会同时被其它执行者访问。这就是通过任务队列来实现异步通信的多线程模型的设计哲学。

       当然,这并不是说,基于任务队列的线程运行模式可以完全避免使用锁,因为任务队列本身就是一个线程间的共享资源。想象一下,一个线程要往里面添加任务,另一个线程要从里面将任务提取出来处理。因此,所有涉及到任务队列访问的地方都是需要加锁的。但是如果我们再仔细想想,那么就会发现,任务队列只是一个基础设施,它与具体的问题是无关的。因此,只要我们遵循上述设计哲学,就可以将代码里面需要加锁的地方仅限于访问任务队列的地方,从而就可以减少锁竞争带来的额外的开销。

       这样说来,似乎基于任务队列的线程运行模式很好,但是实际上它对问题建模提出了更高的要求,也就是进行子问题划分时,要求划分出来的子问题是正交的,这样我们才有可能为这些子问题设计出不会同时被访问的数据结构。看到“正交”两个字,是不是想起高数里面的向量空间的正交基了?或者傅里叶变换用到的一组三角函数了?其实道理就是一样一样的。

       好了,说了这么多,我们就步入到正题,分析Chromium多线程模型的设计和实现,也就是基于任务队列的线程运行模式涉及到核心类图,如图3所示:


图3 基于任务队列的线程运行模式核心类关系图

       Thread是一个用来创建带消息循环的类。当我们创建一个Thread对象后,调用它的成员函数Start或者StartWithOptions就可以启动一个带消息循环的线程。其中,成员函数StartWithOptions可以指定线程创建参数。当我们不需要这个线程时,就可以调用之前创建的Thread对象的成员函数Stop。

       Thread类继承了PlatformThread::Delegate类,并且重写了它的成员函数ThreadMain。我们知道,Chromium是跨平台的,这样各个平台创建线程使用的API有可能是不一样的。不过,我们可以通过PlatformThread::Delegate类为各个平台创建的线程提供一个入口点。这个入口点就是PlatformThread::Delegate类的成员函数ThreadMain。由于Thread类重写了父类PlatformThread::Delegate的成员函数ThreadMain,因此无论是哪一个平台,当它创建完成一个线程后,都会以Thread类的成员函数ThreadMain作为线程的入口点。

       Thread类有一个重要的成员变量message_loop_,它指向的是一个MessageLoop对象。这个MessageLoop对象就是用来描述线程的消息循环的。MessageLoop类内部通过成员变量run_loop_指向的一个RunLoop对象和成员变量pump_指向的一个MessagePump对象来描述一个线程的消息循环。

       一个线程在运行的过程中,可以有若干个消息循环,也就是一个消息循环可以运行在另外一个消息循环里面。除了最外层的消息循环,其余的消息的消息循环称为嵌套消息循环。我们为什么需要嵌套消息循环呢?这主要是跟模式对话框有关。

       考虑一个情景,我们在一个窗口弹出一个文件选择对话框。窗口必须要等到用户在文件选择对话框选择了文件之后,才能去做其它事情。窗口是在消息循环过程中打开文件对话框的,它要等待用户在文件选择对话框中选择文件 ,就意味着消息循环被中止了。由于文件选择对话框也是通过消息循环来响应用户输入的,因此如果打开的它窗口中止了消息循环,就会导致它无法响应用户输入。为了解决这个问题,就要求打开文件选择的窗口不能中止消息循环。方法就是该窗口创建一个子消息循环,该子消息循环负责处理文件选择对应框的输入事件,直到用户选择了一个文件为止。

       MessageLoop类的成员变量run_loop_指向的一个RunLoop对象就是用来记录线程当使用的消息循环的。RunLoop类有三个重要的成员变量:

       1. message_loop_,记录一个RunLoop对象关联的MessageLoop对象。

       2. previous_loop_,记录前一个消息循环,当就是包含当前消息循环的消息循环。

       3. run_depth_,记录消息循环的嵌套深度。

       MessageLoop类的成员变量pump_指向的一个MessagePump对象是用来进行消息循环的,也就是说,Thread类描述的线程通过MessagePump类进入到消息循环中去。

       Thread类将消息划分为三类,分别通过以下三个成员变量来描述:

       1. work_queue_,指向一个TaskQueue对象,用来保存那些需要马上处理的消息。

       2. delayed_work_queue_,指向一个DelayedTaskQueue,用来保存那些需要延迟一段时间再处理的消息。

       3. deferred_non_nestable_work_queue_,指向一个TaskQueue对象,用来保存那些不能够在嵌套消息循环中处理的消息。

       一个MessagePump对象在进行消息循环时,如果发现消息队列中有消息,那么就需要通知关联的MessageLoop对象进行处理。通知使用的接口就通过MessagePump::Delegate类来描述。

       MessagePump::Delegate类定义了四个成员函数,如下所示:

       1. DoWork,用来通知MessageLoop类处理其成员变量work_queue_保存的消息。

       2. DoDelayedWork,用来通知MessageLoop类处理其成员变量delayed_work_queue_保存的消息。

       3. DoIdleWork,用来通知MessageLoop类当前无消息需要处理,MessageLoop类可以利用该间隙做一些Idle Work。

       4. GetQueueingInformation,用来获取MessageLoop类内部维护的消息队列的信息,例如消息队列的大小,以及下一个延迟消息的处理时间。

       有了前面的基础知识,接下来我们就可以大概描述Thread类描述的线程的执行过程。

       首先是线程的启动过程:

       1. 调用Thread类的成员函数Start或者StartWithOptions启动一个线程,并且以Thread类的成员函数ThreadMain作为入口点。

       2. Thread类的成员函数ThreadMain负责创建消息循环,也就是通过MessageLoop类创建消息循环。

       3. MessageLoop类在创建消息循环的过程中,会通过成员函数Init创建用来一个用来消息循环的MessagePump对象。

       4. 消息循环创建完成之后,调用MessageLoop类的成员函数Run进入消息循环。

       5. MessageLoop类的成员函数Run创建一个RunLoop对象,并且调用它的成员函数Run进入消息循环。注意,该RunLoop对象在创建的过程,会关联上当前线程使用的消息循环,也就是创建它的MessageLoop对象。

       6. RunLoop类的成员函数Run负责建立好消息循环的嵌套关系,也就是设置好它的成员变量previous_loop_和run_depth_等,然后就会调用其关联的MessageLoop对象的成员函数RunHandler进入消息循环。

       7. MessageLoop类的成员函数RunHandler调用成员变量pump_描述的一个MessagePump对象的成员函数Run进入消息循环。

       接下来是向线程的消息队列发送消息的过程。这是通过MessageLoop类的以下四个成员函数向消息队列发送消息的:

       1. PostTask,发送需要马上进行处理的并且可以在嵌套消息循环中处理的消息。

       2. PostDelayedTask,发送需要延迟处理的并且可以在嵌套消息循环中处理的消息。

       3. PostNonNestableTask,发送需要马上进行处理的并且不可以在嵌套消息循环中处理的消息。

       4. PostNonNestableDelayedTask,发送需要延迟处理的并且不可以在嵌套消息循环中处理的消息。

       向线程的消息队列发送了新的消息之后,需要唤醒线程,这是通过调用MessagePump类的成员函数Schedule进行的。线程被唤醒之后 ,就会分别调用MessageLoop类重写父类MessagePump::Delegate的两个成员函数DoWork和DoDelayedWork对消息队列的消息进行处理。如果没有消息可以处理,就调用MessageLoop类重写父类MessagePump::Delegate的成员函数DoIdleWork通知线程进入Idle状态,这时候线程就可以做一些Idle Work。

       MessageLoop类的成员函数DoWork在处理消息的过程中,按照以下三个类别进行处理:

       1. 对于可以马上处理的消息,即保存在成员变量work_queue_描述的消息队列的消息,执行它们的成员函数Run。

       2. 对于需要延迟处理的消息,将它们保存在成员变量delayed_work_queue_描述的消息队列中,并且调用成员变量pump_指向的一个MessagePump对象的成员函数ScheduleDelayedWork设置最早一个需要处理的延迟消息的处理时间,以便该MessagePump对象可以优化消息循环逻辑。

       3. 对于可以马上处理但是不可以在嵌套消息循环中处理的消息,如果线程是处理嵌套消息循环中,那么将它们保存在成员变量deferred_non_nestable_work_queue_描述的消息队列中,这些消息将会在线程进入Idle状态时,并且是处理最外层消息循环时,得到处理。

       以上就是Thread类描述的线程的大概执行过程,接下来我们通过源码分析详细描述这些过程。

       我们首先看线程的启动过程,即Thread类的成员函数Start的实现,如下所示:

bool Thread::Start() {
  Options options;
  ......
  return StartWithOptions(options);
}

       这个函数定义在文件external/chromium_org/base/threading/thread.cc中。

       Thread类的成员函数Start调用另外一个成员函数StartWithOptions来启动一个线程,后者可以通过一个类型为Options的参数指定线程的启动参数,这里没有指定,意味着采用默认参数启动一个线程。

       Thread类的成员函数StartWithOptions的实现如下所示:

bool Thread::StartWithOptions(const Options& options) {
  ......

  StartupData startup_data(options);
  startup_data_ = &startup_data;

  if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
    ......
    return false;
  }

  // Wait for the thread to start and initialize message_loop_
  base::ThreadRestrictions::ScopedAllowWait allow_wait;
  startup_data.event.Wait();

  // set it to NULL so we don't keep a pointer to some object on the stack.
  startup_data_ = NULL;
  started_ = true;

  ......
  return true;
}
       这个函数定义在文件external/chromium_org/base/threading/thread.cc中。

       Thread类的成员函数StartWithOptions首先是将线程启动参数封装一个在栈上分配的StartupData对象中,并且这个StartupData对象的地址会保存在Thread类的成员变量startup_data_中。接下来再调用由平台实现的PlatformThread类的静态成员函数Create创建一个线程。最后通过上面封装的StartupData对象的成员变量event描述的一个WaitableEvent对象等待上述创建的线程启动完成。

       一般情况下,线程是不可以进入等待状态的,因为这样会降低线程的响应性。但是有时候线程不得不进入等待状态,例如现在这个情况,当前线程必须要等新创建的线程启动完成之后才能返回,否则的话有可能新创建的线程还没有启动完成,前面在栈上分配的StartupData对象就已经被释放,这样会导致新创建的线程无法访问它的启动参数。

       当新创建的线程启动完成之后,就会通过上述的WaitableEvent对象唤醒当前线程,当前线程将Thread类的成员变量startup_data_置为NULL,避免它引用一个即将无效的在栈上分配的StartupData对象,并且将Thread类的成员变量started_的值设置为true,表示新创建的线程已经启动完毕。

       接下来我们继续分析PlatformThread类的静态成员函数Create的实现。以Android平台为例,它的实现如下所示:

bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
                            PlatformThreadHandle* thread_handle) {
  base::ThreadRestrictions::ScopedAllowWait allow_wait;
  return CreateThread(stack_size, true /* joinable thread */,
                      delegate, thread_handle, kThreadPriority_Normal);
}
      这个函数定义在文件external/chromium_org/base/threading/platform_thread_posix.cc中。

      PlatformThread类的静态成员函数Create调用了另外一个函数CreateThread来创建一个线程,后者的实现如下所示:

bool CreateThread(size_t stack_size, bool joinable,
                  PlatformThread::Delegate* delegate,
                  PlatformThreadHandle* thread_handle,
                  ThreadPriority priority) {
  ......

  bool success = false;
  pthread_attr_t attributes;
  pthread_attr_init(&attributes);

  // Pthreads are joinable by default, so only specify the detached
  // attribute if the thread should be non-joinable.
  if (!joinable) {
    pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
  }

  // Get a better default if available.
  if (stack_size == 0)
    stack_size = base::GetDefaultThreadStackSize(attributes);

  if (stack_size > 0)
    pthread_attr_setstacksize(&attributes, stack_size);

  ThreadParams params;
  params.delegate = delegate;
  params.joinable = joinable;
  params.priority = priority;
  params.handle = thread_handle;

  pthread_t handle;
  int err = pthread_create(&handle,
                           &attributes,
                           ThreadFunc,
                           &ms);
  success = !err;
  ......

  pthread_attr_destroy(&attributes);

  .....

  return success;
}
       这个函数定义在文件external/chromium_org/base/threading/platform_thread_posix.cc中。

       从这里就可以看到,Android平台调用POSIX线程库中的函数pthread_create创建了一个线程,并且指定新创建的线程的入口点函数为ThreadFunc,同时传递给该入口点函数的参数为一个ThreadParams对象,该ThreadParams对象封装了线程启动过程中需要使用到的一系列参数。    

       新创建线程的入口点函数ThreadFunc的实现如下所示:

void* ThreadFunc(void* params) {
  ......
  ThreadParams* thread_params = static_cast<ThreadParams*>(params);

  PlatformThread::Delegate* delegate = thread_params->delegate;
  ......

  delegate->ThreadMain();

  ......
  return NULL;
}
       这个函数定义在文件external/chromium_org/base/threading/platform_thread_posix.cc中。

       函数ThreadFunc首先将参数params转换为一个ThreadParams对象。有了这个ThreadParams对象之后,就可以通过它的成员变量delegate获得一个PlatformThread::Delegate对象。从前面的调用过程可以知道,这个PlatformThread::Delegate对象实际上是一个Thread对象,用来描述新创建的线程。得到了用来描述新创建线程的Thread对象之后,就可以调用它的成员函数ThreadMain继续启动线程了。

       Thread类的成员函数ThreadMain的实现如下所示:

void Thread::ThreadMain() {
  {
    ......
    scoped_ptr<MessageLoop> message_loop;
    if (!startup_data_->options.message_pump_factory.is_null()) {
      message_loop.reset(
          new MessageLoop(startup_data_->options.message_pump_factory.Run()));
    } else {
      message_loop.reset(
          new MessageLoop(startup_data_->options.message_loop_type));
    }

    ......

    message_loop_ = message_loop.get();

    Init();

    running_ = true;
    startup_data_->event.Signal();

    ......

    Run(message_loop_);
    running_ = false;

    ......

    message_loop_ = NULL;
  }
}
       这个函数定义在文件external/chromium_org/base/threading/thread.cc中。

       回忆前面分析的Thread类的成员函数StartWithOptions,它已经将用来描述线程启动参数的一个Options对象保存在成员变量startup_data_描述的一个StartupData对象中,因此我们就可以重新获取这个Options对象。

       当Options类的成员变量message_pump_factory不等于NULL时,就表示新创建线程使用的Message Pump通过该成员变量描述的一个Callback对象来创建,也就是调用该Callback对象的成员函数Run来创建。关于Chromium的Callback机制,可以参考前面Chromium多线程通信的Closure机制分析一文。有了Message Pump之后,就可以创建一个Message Loop了。该Message Loop最终会保存在Thread类的成员变量message_loop_中。

       一般我们不通过Options类的成员变量message_pump_factory来创建Message Pump,而是通过另外一个成员变量message_loop_type来创建指定Message Loop的类型 ,从而确定要创建的Message Pump,这些逻辑都封装在MessageLoop类的构造函数中。

       创建好Message Loop之后,线程的启动工作就完成了,接下来新创建的线程就需要进入到初始化状态,这是通过调用Thread类的成员函数Init实现的。Thread类的成员函数Init一般由子类重写,这样子类就有机会执行一些线程初始化工作。

       再接下来,新创建的线程就需要进入运行状态,这是通过调用Thread类的成员函数Run实现的。不过在新创建线程进入运行状态之前,还会做两件事情。第一件事情是将Thread类的成员变量running_设置为true,表示新创建的线程正在运行。第二件事情是通过Thread类的成员变量startup_data_指向的一个StartupData对象的成员变量event描述的一个WaitableEvent唤醒请求创建新线程的线程。

       最后,当Thread类的成员函数Run执行完成返回后,需要将Thread类的成员变量running_和message_loop_分别重置为false和NULL,表示新创建的线程已经运行结束了,因此就不再需要Message Loop了。

       接下来我们首先分析线程的Message Loop的创建过程,也就是MessageLoop类的构造函数的实现,以完成线程的启动过程,然后再分析线程的运行过程,也就是Thread类的成员函数Run的实现。

       我们假设线程的Message Loop是通过Message Loop Type来创建的,对应的MessageLoop类构造函数的实现如下所示:

MessageLoop::MessageLoop(Type type)
    : type_(type),
      nestable_tasks_allowed_(true),
      ......
      run_loop_(NULL) {
  Init();

  pump_ = CreateMessagePumpForType(type).Pass();
}

       这个函数定义在文件external/chromium_org/base/message_loop/message_loop.cc。 

       MessageLoop类的成员变量type_描述的是消息循环的类型,nestable_tasks_allowed_描述当前是否允许处理嵌套消息,runn_loop_描述的是当前使用的消息循环。 

       MessageLoop类构造函数首先是调用成员函数Init执行初始化工作,接着再调用成员函数CreateMessagePumpForType根据消息循环的类型创建一个Message Pump。接下来我们就分别分析这两个成员函数的实现。

       MessageLoop类的成员函数Init的实现如下所示:

LazyInstance<base::ThreadLocalPointer<MessageLoop> >::Leaky lazy_tls_ptr =
    LAZY_INSTANCE_INITIALIZER;

......

void MessageLoop::Init() {
  ......
  lazy_tls_ptr.Pointer()->Set(this);

  incoming_task_queue_ = new internal::IncomingTaskQueue(this);
  message_loop_proxy_ =
      new internal::MessageLoopProxyImpl(incoming_task_queue_);
  thread_task_runner_handle_.reset(
      new ThreadTaskRunnerHandle(message_loop_proxy_));
}
      这个函数定义在文件external/chromium_org/base/message_loop/message_loop.cc中。

      MessageLoop类的成员函数Init首先将当前创建的MessageLoop对象保存在全局变量lazy_tls_ptr指向一块线程局部存储中,这样我们就可以通过MessageLoop类的静态成员函数current获得当前线程的消息循环,如下所示:

MessageLoop* MessageLoop::current() {
  ......
  return lazy_tls_ptr.Pointer()->Get();
}
      这个函数定义在文件external/chromium_org/base/message_loop/message_loop.cc中。

      回到MessageLoop类的成员函数Init中,接下来它创建了一个任务队列,并且保存在成员变量incoming_queue_中。这个任务队列通过IncomingQueue类来描述,它的定义如下所示:

class BASE_EXPORT IncomingTaskQueue
    : public RefCountedThreadSafe<IncomingTaskQueue> {
 public:
  ......

  bool AddToIncomingQueue(const tracked_objects::Location& from_here,
                          const Closure& task,
                          TimeDelta delay,
                          bool nestable);

  ......
 
  void ReloadWorkQueue(TaskQueue* work_queue);

  ......

  void WillDestroyCurrentMessageLoop();
 
  ......

 private:
  ......

  TaskQueue incoming_queue_;

  ......

  MessageLoop* message_loop_;

  ......
};
       这个类定义在external/chromium_org/base/message_loop/incoming_task_queue.h中。

       IncomingQueue类有两个重要的成员变量:

       1. incoming_queue_,它描述的是一个TaskQueue,代表的是线程的消息队列,也就是所有发送给线程的消息都保存在这里。

       2. message_loop_,它指向一个MessageLoop对象,描述的是线程的消息循环。

       IncomingQueue类有三个重要的成员函数:

       1. AddToIncomingQueue,用来向成员变量incoming_queue_描述的消息队列发送一个消息,并且唤醒线程进行处理。

       2. ReloadWorkQueue,用来提取成员变量incoming_queue_描述的消息队列中的消息,并且保存在参数work_queue中。

       3. WillDestroyCurrentMessageLoop,当该函数被调用时,会将成员变量message_loop_的值设置为NULL,使得我们不能够再向线程发送消息,也就是请求线程执行某一个操作。

       IncomingQueue类的上述成员变量和成员函数我们后面分析消息的发送和处理再详细分析。现在返回到MessageLoop类的成员函数Init中,它接下来创建了一个MessageLoopProxyImpl对象和一个ThreadTaskRunnerHandle对象,分别保存在成员变量message_loop_proxy_和thread_task_runner_handle中,前者封装了当前线程的消息队列,后者又封装了前者。它们与MessageLoop类一样,都是可以用来向线程的消息队列发送消息,这意味着我们有三种方式向线程的消息队列发送消息,后面分析消息的发送过程时我们再详细分析。

       MessageLoop类的成员函数Init执行完成后,回到MessageLoop类的构造函数中,接下来它调用另外一个成员函数CreateMessagePumpForType根据消息循环的类型创建一个消息泵(Message Pump),并且保存在成员变量pump_中。

       MessageLoop类的成员函数CreateMessagePumpForType的实现如下所示:

#if defined(OS_IOS)
typedef MessagePumpIOSForIO MessagePumpForIO;
#elif defined(OS_NACL)
typedef MessagePumpDefault MessagePumpForIO;
#elif defined(OS_POSIX)
typedef MessagePumpLibevent MessagePumpForIO;
#endif
......

scoped_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) {
  ......

#if defined(OS_IOS) || defined(OS_MACOSX)
#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(MessagePumpMac::Create())
#elif defined(OS_NACL)
// Currently NaCl doesn't have a UI MessageLoop.
// TODO(abarth): Figure out if we need this.
#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>()
#else
#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(new MessagePumpForUI())
#endif

  if (type == MessageLoop::TYPE_UI) {
    if (message_pump_for_ui_factory_)
      return message_pump_for_ui_factory_();
    return MESSAGE_PUMP_UI;
  }
  if (type == MessageLoop::TYPE_IO)
    return scoped_ptr<MessagePump>(new MessagePumpForIO());

#if defined(OS_ANDROID)
  if (type == MessageLoop::TYPE_JAVA)
    return scoped_ptr<MessagePump>(new MessagePumpForUI());
#endif

  ......
  return scoped_ptr<MessagePump>(new MessagePumpDefa