目录
定时器事件,顾名思义就是定时事件到了,于是开始执行的事件.
一 定时器事件的创建
定时器事件的创建是用htimer_add()函数来进行创建的,loop参数表示事件回环,cb表示定时器事件触发时的回调函数,timeout表示定时器定时周期,repeat指的是定时器事件重复触发次数:
1.首先在堆区创建定时器事件变量timer,设置该事件类型为定时器超时事件,事件优先级为最高优先级,重复次数为repeat次,定时周期为timeout,设置下一次定时超时时间.
2.将timer事件加入到loop中以loop->timers为堆顶所建立的堆(一般堆中的节点都是父节点的值小于以父节点为根节点所建立的树的所有节点的值)中.此处可以类比idle事件中loop->idles的链表(虽然不恰当,但是都是为了存储这一类的事件).
3.添加timer事件:激活timer事件并且设置回调函数.
说白了,htimer_add()函数是建立一个timer事件,赋予它超时时间,超时次数,下一次超时的绝对时间,并且将其加入loop->timers堆中,最后激活timer事件,设置回调函数.
htimer_t* htimer_add(hloop_t* loop, htimer_cb cb, uint32_t timeout, uint32_t repeat) {
if (timeout == 0) return NULL;
htimeout_t* timer;
HV_ALLOC_SIZEOF(timer);
timer->event_type = HEVENT_TYPE_TIMEOUT;
timer->priority = HEVENT_HIGHEST_PRIORITY;
timer->repeat = repeat;
timer->timeout = timeout;
hloop_update_time(loop);//排除由于修改系统时间而导致的时间偏差
//设置的下次超时时间指指的是以一天为基准,毫秒为单位设置的超时时间
timer->next_timeout = hloop_now_hrtime(loop) + (uint64_t)timeout*1000;
// NOTE: Limit granularity to 100ms
if (timeout >= 1000 && timeout % 100 == 0) {
timer->next_timeout = timer->next_timeout / 100000 * 100000;
}
heap_insert(&loop->timers, &timer->node);
EVENT_ADD(loop, timer, cb);
loop->ntimers++;
return (htimer_t*)timer;
}
二 定时器事件的执行
不管是什么事件,我们都是在hloop_process_events()函数中间接调用的.对于定时器事件,该函数会首先判断loop->timers堆中是否存在超时的timer节点(如下图截取的hloop_process_events()函数中的一段代码),如果存在再直接跳过对I/O事件的执行,直接跳转到hloop_process_timers()函数中执行定时器事件:
int32_t blocktime = HLOOP_MAX_BLOCK_TIME;
if (loop->timers.root) {
hloop_update_time(loop);
uint64_t next_min_timeout = TIMER_ENTRY(loop->timers.root)->next_timeout;
int64_t blocktime_us = next_min_timeout - hloop_now_hrtime(loop);
if (blocktime_us <= 0) goto process_timers;
blocktime = blocktime_us / 1000;
++blocktime;
blocktime = MIN(blocktime, HLOOP_MAX_BLOCK_TIME);
}
如下再展示定时器事件的执行(由 goto process_timers这句话跳转过来的):
static int hloop_process_timers(hloop_t* loop) {
int ntimers = 0;
htimer_t* timer = NULL;
uint64_t now_hrtime = hloop_now_hrtime(loop);
while (loop->timers.root) {
// NOTE: root of minheap has min timeout.
timer = TIMER_ENTRY(loop->timers.root);
if (timer->next_timeout > now_hrtime) {
break;
}
if (timer->repeat != INFINITE) {
--timer->repeat;
}
if (timer->repeat == 0) {
// NOTE: Just mark it as destroy and remove from heap.
// Real deletion occurs after hloop_process_pendings.
__htimer_del(timer);
}
else {
// NOTE: calc next timeout, then re-insert heap.
heap_dequeue(&loop->timers);
if (timer->event_type == HEVENT_TYPE_TIMEOUT) {
while (timer->next_timeout <= now_hrtime) {
timer->next_timeout += (uint64_t)((htimeout_t*)timer)->timeout * 1000;
}
}
else if (timer->event_type == HEVENT_TYPE_PERIOD) {
hperiod_t* period = (hperiod_t*)timer;
timer->next_timeout = (uint64_t)cron_next_timeout(period->minute, period->hour, period->day,
period->week, period->month) * 1000000;
}
heap_insert(&loop->timers, &timer->node);
}
EVENT_PENDING(timer);
++ntimers;
}
return ntimers;
}
hloop_process_timers()函数的执行过程:
1.获取当前时间(以天为基准,微秒为单位的当前时间);
2.反复查询堆中最顶部(最顶部timer节点的超时时间最早),如果超时了,则将超时事件重复次数减一,如果减一以后重复次数为0,表示当前timer节点是最后一次触发,移除该timers事件(从堆中移除该timer事件,并且设置timer->destroy等于1),最后再重新计算timer事件下一次超时事件,再重新添加到loop->timers堆中,然后将该timer事件添加到待处理事件集合中.
最后,在hloop_process_events()函数的末尾调用hloop_process_pendings()函数对待处理事件集合中的所有事件进行处理(执行响应回调函数).
三 定时器事件的销毁
定时器事件的销毁有两种方法:1.循环调用hloop_process_events()函数 ,直到定时器事件重复加入待处理集合并且处理完repeat次为止;2.调用htimer_del()函数手动删除本idle事件.
static void __htimer_del(htimer_t* timer) {
if (timer->destroy) return;
heap_remove(&timer->loop->timers, &timer->node);
timer->loop->ntimers--;
timer->destroy = 1;
}
void htimer_del(htimer_t* timer) {
if (!timer->active) return;
__htimer_del(timer);
EVENT_DEL(timer);
}
四 定时器事件执行顺序
1.利用htimer_add()函数创建定时器事件;
2.调用hloop_process_events()函数处理定时器事件:
a.调用hloop_process_idles()函数将循环遍历定时器堆中将定时器事件加入待处理事件集合;
b.调用hloop_process_pendings()函数处理待处理事件集合中的事件;
3.选择性执行htimer_del()函数删除不需要且重复次数还未完成的定时器事件.