chromium源码阅读--进程的Message Loop
上一篇总结了chromium进程的启动,接下来就看线程的消息处理,这里的线程包含进程的主线程。
消息处理是由base::MessageLoop中实现,消息中的任务和定时器都是异步事件的。
主要如下几点:
1、消息的类型分类
2、延时处理的消息是如何实现
一、消息分类
chromium主要将消息类型如下定义:(chromium//src/base/message_loop/message_loop.h 112行)
enum Type {
TYPE_DEFAULT,
TYPE_UI,
TYPE_CUSTOM,
TYPE_IO,
#if defined(OS_ANDROID)
TYPE_JAVA,
#endif // defined(OS_ANDROID)
};
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行
// static
std::unique_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) {
// TODO(rvargas): Get rid of the OS guards.
#if defined(USE_GLIB) && !defined(OS_NACL)
using MessagePumpForUI = MessagePumpGlib;
#elif (defined(OS_LINUX) && !defined(OS_NACL)) || defined(OS_BSD)
using MessagePumpForUI = MessagePumpLibevent;
#elif defined(OS_FUCHSIA)
using MessagePumpForUI = MessagePumpFuchsia;
#endif #if defined(OS_IOS) || defined(OS_MACOSX)
#define MESSAGE_PUMP_UI std::unique_ptr<MessagePump>(MessagePumpMac::Create())
#elif defined(OS_NACL) || defined(OS_AIX)
// Currently NaCl and AIX don't have a UI MessageLoop.
// TODO(abarth): Figure out if we need this.
#define MESSAGE_PUMP_UI std::unique_ptr<MessagePump>()
#else
#define MESSAGE_PUMP_UI std::unique_ptr<MessagePump>(new MessagePumpForUI())
#endif #if defined(OS_MACOSX)
// Use an OS native runloop on Mac to support timer coalescing.
#define MESSAGE_PUMP_DEFAULT \
std::unique_ptr<MessagePump>(new MessagePumpCFRunLoop())
#else
#define MESSAGE_PUMP_DEFAULT \
std::unique_ptr<MessagePump>(new MessagePumpDefault())
#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 std::unique_ptr<MessagePump>(new MessagePumpForIO()); #if defined(OS_ANDROID)
if (type == MessageLoop::TYPE_JAVA)
return std::unique_ptr<MessagePump>(new MessagePumpForUI());
#endif DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type);
return MESSAGE_PUMP_DEFAULT;
}
二、延时消息如何处理
消息的处理与消息队列密不可分,internal::IncomingTaskQueue实现了一个线程安全的消息队列。 MessageLoop里定义了(chromium//src/base/message_loop/message_loop.h 392行)
scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;
接收到的消息就缓存在这个队列里。那么我们先看看这个类的构造函数。
IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop)
: always_schedule_work_(AlwaysNotifyPump(message_loop->type())),
triage_tasks_(this),
delayed_tasks_(this),
deferred_tasks_(this),
message_loop_(message_loop) {
// The constructing sequence is not necessarily the running sequence in the
// case of base::Thread.
DETACH_FROM_SEQUENCE(sequence_checker_);
}
构造函数里通过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函数:
bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
bool accept_new_tasks;
bool schedule_work = false;
{
AutoLock auto_lock(incoming_queue_lock_);
accept_new_tasks = accept_new_tasks_;
if (accept_new_tasks)
schedule_work = PostPendingTaskLockRequired(pending_task);
} if (!accept_new_tasks) {
pending_task->task.Reset();
return false;
} if (schedule_work) {
// Ensures |message_loop_| isn't destroyed while running.
AutoLock auto_lock(message_loop_lock_);
if (message_loop_)
message_loop_->ScheduleWork();
} return true;
}
这里已经开始给线程加锁了,那么继续看PostPendingTaskLockRequired函数:
bool IncomingTaskQueue::PostPendingTaskLockRequired(PendingTask* pending_task) {
incoming_queue_lock_.AssertAcquired();
...... pending_task->sequence_num = next_sequence_num_++; task_annotator_.DidQueueTask("MessageLoop::PostTask", *pending_task); bool was_empty = incoming_queue_.empty();
incoming_queue_.push(std::move(*pending_task)); if (is_ready_for_scheduling_ &&
(always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
message_loop_scheduled_ = true;
return true;
}
return false;
}
这里看到pending_task是保存在incoming_queue_ 这里使用了std::queue容器(一个FIFO的数据结构),这个队列里面的任务还没有添加到MessageLoop中,也可以看到这里还没有明确任务的执行方式,使用的是FIFO队列。
下面的几个成员变量,则就是在MessageLoop中使用了。
chromium//src/base/message_loop/incoming_task_queue.h 217行
// Queue for initial triaging of tasks on the |sequence_checker_| sequence.
TriageQueue triage_tasks_; // Queue for delayed tasks on the |sequence_checker_| sequence.
DelayedQueue delayed_tasks_; // Queue for non-nestable deferred tasks on the |sequence_checker_| sequence.
DeferredQueue deferred_tasks_;
1、TriageQueue
这是第一个按默认的任务处理顺序(FIFO)接受所有任务的队列,这个队列的任务要马上执行或者放到下面的DelayedQueue 或者 DeferredQueue。
triage_tasks_队列的任务是通过 IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue)来实现切换的,可以将 incoming_queue_ 和 triage_tasks_看成冷热备份的缓存,在triage_tasks_队列的任务执行完了,即为空时,就将待执行的incoming_queue_队列的任务与之交换。
void IncomingTaskQueue::TriageQueue::ReloadFromIncomingQueueIfEmpty() {
if (queue_.empty()) {
// TODO(robliao): Since these high resolution tasks aren't yet in the
// delayed queue, they technically shouldn't trigger high resolution timers
// until they are.
outer_->pending_high_res_tasks_ += outer_->ReloadWorkQueue(&queue_);
}
}
2、DelayedQueue
这个队列是存放延迟执行的任务,并且按期望时间排序的
delayed_tasks_是一个优先级队列,按delayed_run_time排序,chromium//src/base/pending_task.h 63行
// PendingTasks are sorted by their |delayed_run_time| property.
using DelayedTaskQueue = std::priority_queue<base::PendingTask>;
3、DeferredQueue
这个队列通常是存放哪些因为MessageLoop嵌套而不能执行的任务,这些任务通常会在空闲的时候执行。
OK,看到这里,我们回顾一下MessageLoop的执行流程:
void MessageLoop::Run() {
DCHECK_EQ(this, current());
pump_->Run(this);
}
由MessagePump来执行,那么我们选择默认的MessagePump来看Run的流程,chromium//src/base/message_loop/message_pump_default.cc 29行:
void MessagePumpDefault::Run(Delegate* delegate) {
AutoReset<bool> auto_reset_keep_running(&keep_running_, true); for (;;) {
#if defined(OS_MACOSX)
mac::ScopedNSAutoreleasePool autorelease_pool;
#endif 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; ThreadRestrictions::ScopedAllowWait allow_wait;
if (delayed_work_time_.is_null()) {
event_.Wait();
} else {
event_.TimedWaitUntil(delayed_work_time_);
}
}
}
其中的流程是,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的创建。
chromium源码阅读--进程的Message Loop的更多相关文章
- [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答
chromium源码阅读-进程间通信IPC.消息的接收与应答 chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...
- Linux 源码阅读 进程管理
Linux 源码阅读 进程管理 版本:2.6.24 1.准备知识 1.1 Linux系统中,进程是最小的调度单位: 1.2 PCB数据结构:task_struct (Location:linux-2. ...
- chromium源码阅读--Browser进程初始化
最近在研读chromium源码,经过一段懵懂期,查阅了官网和网上的技术文章,是时候自己总结一下了,首先IPC message loop开始吧,这是每个主线程必须有的一个IPC消息轮训主体,类似之前的q ...
- Nutch源码阅读进程3---fetch
走了一遍Inject和Generate,基本了解了nutch在执行爬取前的一些前期预热工作,包括url的过滤.规则化.分值计算以及其与mapreduce的联系紧密性等,自我感觉nutch的整个流程是很 ...
- chromium源码阅读--进程间通信(IPC)
第一篇就有提到Chromium是目前默认是采用多进程架构,当然,chromium有singe-process的版本. 多进程与多线程的区别,确实有很多可以讲的,我的另一篇博客也讲了一些,这里是从浏览器 ...
- Nutch源码阅读进程3
走了一遍Inject和Generate,基本了解了nutch在执行爬取前的一些前期预热工作,包括url的过滤.规则化.分值计算以及其与mapreduce的联系紧密性等,自我感觉nutch的整个流程是很 ...
- Nutch源码阅读进程5---updatedb
看nutch的源码仿佛就是一场谍战片,而构成这精彩绝伦的谍战剧情的就是nutch的每一个从inject->generate->fetch->parse->update的环节,首 ...
- Nutch源码阅读进程2---Generate
继之前仓促走完nutch的第一个流程Inject后,再次起航,Debug模式走起,进入第二个预热阶段Generate~~~ 上期回顾:Inject主要是将爬取列表中的url转换为指定格式<T ...
- Nutch源码阅读进程1---inject
最近在Ubuntu下配置好了nutch和solr的环境,也用nutch爬取了一些网页,通过solr界面呈现,也过了一把自己建立小搜索引擎的瘾,现在该静下心来好好看看nutch的源码了,先从Inject ...
随机推荐
- 201521123112《Java程序设计》第6周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...
- 201521123074 《Java程序设计》第4周学习总结
1.本周学习总结 这次用XMind画的图,果然比百度脑图好用一点. 2.书面作业 Q1.注释的应用 使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看.(截图) 类注释 方 ...
- 201521123013 《Java程序设计》第1周学习总结
1. 本章学习总结 1.Java是面向对象的编程语言,它在通过jvm和jre将其转成本地机器代码,达到一次撰写,到处运行的效益,实现跨平台运行,代码开源,使用范围广. 2.了解jdk.jre.jvm的 ...
- evak购物车--课程设计(201521123037邱晓娴)
1. 团队课程设计博客链接 团队博客 2. 个人负责模块或任务说明 1.Java (1)编写用户类Users (2)编写DBConnection类,连接数据库 (3)编写GoodsDAO类,从数据库中 ...
- 201521123037 《Java程序设计》第10周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 创建自己的异常 1.自定义异常要继承已有的异常,需要手动throw异常:根据继承的异常不同以及具体情况,选择 ...
- 201521123087 《Java程序设计》第9周学习总结
1. 本周学习总结 2. 书 面作业 本次PTA作业题集异常 常用异常题目5-11.1 截图你的提交结果(出现学号)1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避免? ...
- Eclipse: eclipse文本文件编码格式更改(GBK——UTF-8)
Eclipse中设置编码的方式 Eclipse工 作空间(workspace)的缺省字符编码是操作系统缺省的编码,简体中文操作系统 (Windows XP.Windows 2000简体中文)的缺省编码 ...
- python函数式编程,列表生成式
1.python 中常见的集中存储数据的结构: 列表 集合 字典 元组 字符串 双队列 堆 其中最常见的就是列表,字典. 2.下面讲一些运用循环获取字典列表的元素 >>> dic={ ...
- Window10中利用Windbg与虚拟机(window7)中调试驱动建立方法
想起自己的windbg配置就转载:eqera的windows内核调试配置,真的是获益良多希望他不会介意我转载他的博客,帮了我很多,记录下来给我也给大家, 其中我主要看的是VMWare的pipe建立,而 ...
- 庞玉栋:浅谈seo优化对于网站建设的重要性
根据最近做SEO优化经验而写 写的也都是我的方法 大神勿喷 SEO:英文Search Engine Optimization缩写而来, 中文意译为搜索引擎优化 如果你连个网站都没有那就点这里:如何拥 ...