事件循环
1. 纠正时间,因为用户可能手动更改本地时间,而超时是针对时间点的,这样导致时间不准确,超时事件受到影响。
2. 获取堆顶元素超时时间,与当前时间相减,得到差值,该差值传给epoll,让其最晚在差值时间后返回。
如果,当前堆顶已经超时了,则直接返回0,让epoll_wait 立即返回。通过这种措施,防止长时间没有IO事件到来,
epoll 不能返回,超时事件得不到处理。
3. 记录时间点1, tv_cache-> event_tv
4. epoll_wait
5. 记录时间点 2 系统调用获取时间 ->tv_cache
6. 处理超时事件
7. 处理激活队列中事件
时间点1的作用:用于时间纠正,此处event_tv 为 tv_cache 的值,在下一轮时间纠正函数处,event_tv < tv_cache的,
如果不是小于,那么进行时间纠正。
缓存时间,可以减少系统调用这样耗时的动作,在时间点2到时间点1的这个阶段,获得的时间都是cache缓存的时间。
所以event_tv作用是缓存tv_cache的值,用于纠正时间。
时间更正
static void
timeout_correct(struct event_base *base, struct timeval *tv)
{
struct event **pev;
unsigned int size;
struct timeval off;
// 采用monotonic 不需要纠正
if (use_monotonic)
return;
/* 由于时间缓存,此处tv 即为 tv_cache值 */
gettime(base, tv);
if (evutil_timercmp(tv, &base->event_tv, >=)) {
base->event_tv = *tv;
return;
}
/* off 为时间差值 */
evutil_timersub(&base->event_tv, tv, &off);
/* 时间根堆所有超时时间都要调整,减去off*/
pev = base->timeheap.p;
size = base->timeheap.n;
for (; size-- > 0; ++pev) {
struct timeval *ev_tv = &(**pev).ev_timeout;
evutil_timersub(ev_tv, &off, ev_tv);
}
/* Now remember what the new time turned out to be. */
base->event_tv = *tv;
}