Webkit中Timer实现的基本思想是: 每个线程维护一个虚拟Timer的优先级队列,每次启动或停止一个虚拟Timer时,都会设置该Timer的下次触发时间(”next fire time”)。当虚拟Timer的触发时间变化时,需要调整其在优先级队列的位置,以保证队列的有效性。当虚拟Timer启动或者先前的系统Timer触发的时候,会调用由具体平台实现的setSharedTimerFireTime函数,去设置系统Timer的等待时间,开始新一轮的超时等待。
图1是webkit定时器相关的类,下面进行简要分析。
其中,Timer是个模板类,继承于TimerBase,它就是所谓的虚拟定时器,提供了具体平台的系统Timer触发时的回调接口,以及启动和停止定时器的相关接口。另一方面,TimerBase提供了优先级队列(由堆结构实现)的访问接口,例如:heapDecreaseKey用于调整堆,以保证当前定时器的下次触发时间缩短后优先级仍然有效;heapDelete从优先级队列中删除当前定时器;heapInsert向优先级队列插入定时器。PS:TimerBase中提供的优先级访问接口直接操纵的是ThreadTimers的m_timerheap成员。
SharedTimer是具体平台Timer实现的抽象,提供了设置具体平台系统Timer的接口。
ThreadTimers维护同一线程内的所有Timer和线程内所有Timer共享的SharedTimer。
TimerHeapElement是个辅助类,方便优先级队列的修改。TimerHeapIterator继承于标准库的iterator,实现了随机迭代器,以便调用标准库的pop_heap和push_heap两个函数对堆进行操作。此外,这两个函数默认的比较运算符是’<’,维护的是最大堆,而这里基于下次触发时间的优先级队列需要最小堆,所以对TimerHeapElement元素的’<’运算符进行了一些处理。
下图是启动定时器的序列图。当启动Timer时,会用当前时间加上等待时间(以秒为单位,精确到毫秒),作为下次定时器触发时间。如图所示,在调用setNextFireTime进行超时时间设置时,会修改优先级队列。例如,该Timer是第一次启动时,调用heapInsert将该Timer插入到队列中;如果Timer已经存在于队列中,那么存在两种情况:1)新设置的超时时间晚于先前的,调用heapIncreaseKey函数对堆进行调整;2)新设置的超时时间早于先前的,调用heapDecreaseKey函数对堆进行调整。设置完下次触发时间后,如果该Timer在堆调整之前或者目前在优先级队列之首,那么调用updateSharedTimer,设置具体平台的系统Timer。
// TimerExample定时器调用的一个实例
Class TimerExample
{
public:
void start();
private:
onTimerFired(Timer<TimerExample>*);
private:
m_timer; //定时器
};
TimerExample:: TimerExample (…)
: m_timer(this, & TimerExample::onTimerFired)
{
//...
}
// 成员函数
void TimerExample::onTimerFired( Timer<TimerExample>*)
{
//...
}
// Timer::startOneShot表示只触发一次, 不用手动stop
// 调用Timer::startRepeating可以让Timer循环触发
void TimerExample::start()
{
//...
m_timer.startOneShot(0);
}