您的位置:首页 > 运维架构

chromium源码阅读--进程的Message Loop

2017-10-19 16:33 495 查看
消息处理是由base::MessageLoop中实现,消息中的任务和定时器都是异步事件的。

主要如下几点:

1、消息的类型分类

2、延时处理的消息是如何实现

 

一、消息分类

     chromium主要将消息类型如下定义:(chromium//src/base/message_loop/message_loop.h 
112行)

1  enum Type {
2     TYPE_DEFAULT,
3     TYPE_UI,
4     TYPE_CUSTOM,
5     TYPE_IO,
6 #if defined(OS_ANDROID)
7     TYPE_JAVA,
8 #endif  // defined(OS_ANDROID)
9   };


1.TYPE_DEFAULT:

      处理chromium定义的Task(闭包代码块)和定时器任务

2.TYPE_UI:

     除了TYPE_DEFAULT定义的范围,还支持原生的UI事件消息(比如用户操作的窗口消息),MessageLoopForUI类

3.TYPE_IO:

     除了TYPE_DEFAULT定义的范围,还支持异步IO的事件消息,MessageLoopForIO类

4.TYPE_JAVA

    是Android平台的特有的消息消息,因为Android里,有java消息和native消息分层,native消息与java消息交互,java消息与应用程序交互,可以看做java消息接管了native消息。

5.TYPE_CUSTOM

    定制消息,比较少见使用。

 

消息类型的不同也就会创建不同的MessagePump。对于UI消息,不同的平台也会有不同的实现。在chromium//src/base/message_loop/message_loop.cc 166行


 View
Code

 

二、延时消息如何处理

消息的处理与消息队列密不可分,internal::IncomingTaskQueue实现了一个线程安全的消息队列。 MessageLoop里定义了(chromium//src/base/message_loop/message_loop.h 392行)

1 scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;


接收到的消息就缓存在这个队列里。那么我们先看看这个类的构造函数。

chromium//src/base/message_loop/incoming_task_queue.cc
52行

1 IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop)
2     : always_schedule_work_(AlwaysNotifyPump(message_loop->type())),
3       triage_tasks_(this),
4       delayed_tasks_(this),
5       deferred_tasks_(this),
6       message_loop_(message_loop) {
7   // The constructing sequence is not necessarily the running sequence in the
8   // case of base::Thread.
9   DETACH_FROM_SEQUENCE(sequence_checker_);
10 }


构造函数里通过MessageLoop的类型来初始化bool成员 always_schedule_work_ ,来判断是否对消息进行调度, 并保存了message_loop指针。

继续分析代码,前面看到消息队列已经初始化了,那接下来我们看看是怎么往队列里添加任务的。

bool IncomingTaskQueue::AddToIncomingQueue(const Location& from_here,
OnceClosure task,
TimeDelta delay,
Nestable nestable) {
......

PendingTask pending_task(from_here, std::move(task),
CalculateDelayedRuntime(delay), nestable);
......
return PostPendingTask(&pending_task);
}


使用了PendingTask对象,并计算了延迟的时间和是否是嵌套任务。那么看PostPendingTask函数:

1 bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
5   bool accept_new_tasks;
6   bool schedule_work = false;
7   {
8     AutoLock auto_lock(incoming_queue_lock_);
9     accept_new_tasks = accept_new_tasks_;
10     if (accept_new_tasks)
11       schedule_work = PostPendingTaskLockRequired(pending_task);
12   }
13
14   if (!accept_new_tasks) {
19     pending_task->task.Reset();
20     return false;
21   }
22
29   if (schedule_work) {
30     // Ensures |message_loop_| isn't destroyed while running.
31     AutoLock auto_lock(message_loop_lock_);
32     if (message_loop_)
33       message_loop_->ScheduleWork();
34   }
35
36   return true;
37 }


这里已经开始给线程加锁了,那么继续看PostPendingTaskLockRequired函数:

1 bool IncomingTaskQueue::PostPendingTaskLockRequired(PendingTask* pending_task) {
2   incoming_queue_lock_.AssertAcquired();
3   ......
4
7   pending_task->sequence_num = next_sequence_num_++;
8
9   task_annotator_.DidQueueTask("MessageLoop::PostTask", *pending_task);
10
11   bool was_empty = incoming_queue_.empty();
12   incoming_queue_.push(std::move(*pending_task));
13
14   if (is_ready_for_scheduling_ &&
15       (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
21     message_loop_scheduled_ = true;
22     return true;
23   }
24   return false;
25 }


这里看到pending_task是保存在incoming_queue_ 这里使用了std::queue容器(一个FIFO的数据结构),这个队列里面的任务还没有添加到MessageLoop中,也可以看到这里还没有明确任务的执行方式,使用的是FIFO队列。

下面的几个成员变量,则就是在MessageLoop中使用了。

chromium//src/base/message_loop/incoming_task_queue.h 217行

1 // Queue for initial triaging of tasks on the |sequence_checker_| sequence.
2   TriageQueue triage_tasks_;
3
4   // Queue for delayed tasks on the |sequence_checker_| sequence.
5   DelayedQueue delayed_tasks_;
6
7   // Queue for non-nestable deferred tasks on the |sequence_checker_| sequence.
8   DeferredQueue deferred_tasks_;


1、TriageQueue

     这是第一个按默认的任务处理顺序(FIFO)接受所有任务的队列,这个队列的任务要马上执行或者放到下面的DelayedQueue 或者 DeferredQueue。

     triage_tasks_队列的任务是通过 IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue)来实现切换的,可以将
incoming_queue_ 和 triage_tasks_看成冷热备份的缓存,在triage_tasks_队列的任务执行完了,即为空时,就将待执行的incoming_queue_队列的任务与之交换。

1 void IncomingTaskQueue::TriageQueue::ReloadFromIncomingQueueIfEmpty() {
2   if (queue_.empty()) {
3     // TODO(robliao): Since these high resolution tasks aren't yet in the
4     // delayed queue, they technically shouldn't trigger high resolution timers
5     // until they are.
6     outer_->pending_high_res_tasks_ += outer_->ReloadWorkQueue(&queue_);
7   }
8 }


2、DelayedQueue

     这个队列是存放延迟执行的任务,并且按期望时间排序的

     delayed_tasks_是一个优先级队列,按delayed_run_time排序,chromium//src/base/pending_task.h
63行

1 // PendingTasks are sorted by their |delayed_run_time| property.
2 using DelayedTaskQueue = std::priority_queue<base::PendingTask>;


3、DeferredQueue

     这个队列通常是存放哪些因为MessageLoop嵌套而不能执行的任务,这些任务通常会在空闲的时候执行。

OK,看到这里,我们回顾一下MessageLoop的执行流程:

1 void MessageLoop::Run() {
2   DCHECK_EQ(this, current());
3   pump_->Run(this);
4 }


由MessagePump来执行,那么我们选择默认的MessagePump来看Run的流程,chromium//src/base/message_loop/message_pump_default.cc 29行:


 View
Code

其中的流程是,delegate->DoWork(), delegate->DoDelayedWork(&delayed_work_time_),delegate->DoIdleWork()。

也可以看其他平台的MessagePump,主要的流程是一致的,都是delegate的函数,而delegate指向一个MessageLoop指针,那么又回到MessageLoop中。

上面具体的DoWork就不详述了,接下来看看延迟任务是如何实现的:

chromium//src/base/message_loop/message_loop.cc
473行

TimeTicks next_run_time =
incoming_task_queue_->delayed_tasks().Peek().delayed_run_time;
if (next_run_time > recent_time_) {
recent_time_ = TimeTicks::Now();  // Get a better view of Now();
if (next_run_time > recent_time_) {
*next_delayed_work_time = next_run_time;
return false;
}
}

PendingTask pending_task = incoming_task_queue_->delayed_tasks().Pop();

if (incoming_task_queue_->delayed_tasks().HasTasks()) {
*next_delayed_work_time =
incoming_task_queue_->delayed_tasks().Peek().delayed_run_time;
}

return DeferOrRunPendingTask(std::move(pending_task));


在下一个可执行时间到来了,就会从incoming_task_queue_->delayed_tasks().Pop() 出来一个task, 如果incoming_task_queue_->delayed_tasks()里还有延迟任务,就取里面优先级最高的任务的延迟时间作为下次判断的时间。

到这里,消息的处理和延迟任务的执行都完成了。

好了,回到上面的Run()函数流程,在DoIdleWork()完成之后,当前线程开始休眠,直到有新的任务来会重新唤醒。

 

嗯,这篇就到这里了,下面一篇会总结IPC消息声明和IPC channel的创建。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: