时间获取
libevent使用两种方式获取时间,首选是通过clock_gettime的CLOCK_MONOTONIC参数获取绝对时间,这个时间是递增的,不受系统时间影响,比较准确。其次是使用gettimeofday或者_ftime,这两种方式都是获取系统时间,下面是相关代码:
static int
gettime(struct event_base *base, struct timeval *tp)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (base->tv_cache.tv_sec) {
*tp = base->tv_cache;
return (0);
}
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
if (use_monotonic) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
return (-1);
tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000;
if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL
< ts.tv_sec) {
struct timeval tv;
evutil_gettimeofday(&tv,NULL);
evutil_timersub(&tv, tp, &base->tv_clock_diff);
base->last_updated_clock_diff = ts.tv_sec;
}
return (0);
}
#endif
return (evutil_gettimeofday(tp, NULL));
}
// 获取系统时间的方法
#ifdef _EVENT_HAVE_GETTIMEOFDAY
#define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz))
#else
int
evutil_gettimeofday(struct timeval *tv, struct timezone *tz)
{
struct _timeb tb;
if (tv == NULL)
return -1;
/* XXXX
* _ftime is not the greatest interface here; GetSystemTimeAsFileTime
* would give us better resolution, whereas something cobbled together
* with GetTickCount could maybe give us monotonic behavior.
*
* Either way, I think this value might be skewed to ignore the
* timezone, and just return local time. That's not so good.
*/
_ftime(&tb);
tv->tv_sec = (long) tb.time;
tv->tv_usec = ((int) tb.millitm) * 1000;
return 0;
}
#endif
在gettime方法中会首选使用CLOCK_MONOTONIC方式返回时间,其次才是使用evutil_gettimeofday获取系统时间,当时用CLOCK_MONOTONIC时,libevent会在event_base中存储绝对时间和系统时间的差值。event_base中的tv_cache存储的也是通过调用gettime设置的,所以如果系统支持CLOCK_MONOTONIC,则tv_cache中存储的是绝对时间,此时如果希望通过该缓存获取系统时间,则应该加上tv_clock_diff:
int
event_base_gettimeofday_cached(struct event_base *base, struct timeval *tv)
{
int r;
if (!base) {
base = current_base;
if (!current_base)
return evutil_gettimeofday(tv, NULL);
}
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (base->tv_cache.tv_sec == 0) {
r = evutil_gettimeofday(tv, NULL);
} else {
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
evutil_timeradd(&base->tv_cache, &base->tv_clock_diff, tv);
#else
*tv = base->tv_cache;
#endif
r = 0;
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
return r;
}
另外libevent每次调用loop循环之前都要检测系统时间是否向前调整过,如果调整过则需要调整小根堆和commonlist中时间的设置:
static void
timeout_correct(struct event_base *base, struct timeval *tv)
{
/* Caller must hold th_base_lock. */
struct event **pev;
unsigned int size;
struct timeval off;
int i;
if (use_monotonic)
return;
/* Check if time is running backwards */
gettime(base, tv);
if (evutil_timercmp(tv, &base->event_tv, >=)) {
base->event_tv = *tv;
return;
}
event_debug(("%s: time is running backwards, corrected",
__func__));
evutil_timersub(&base->event_tv, tv, &off);
/*
* We can modify the key element of the node without destroying
* the minheap property, because we change every element.
*/
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);
}
for (i=0; i<base->n_common_timeouts; ++i) {
struct event *ev;
struct common_timeout_list *ctl =
base->common_timeout_queues[i];
TAILQ_FOREACH(ev, &ctl->events,
ev_timeout_pos.ev_next_with_common_timeout) {
struct timeval *ev_tv = &ev->ev_timeout;
ev_tv->tv_usec &= MICROSECONDS_MASK;
evutil_timersub(ev_tv, &off, ev_tv);
ev_tv->tv_usec |= COMMON_TIMEOUT_MAGIC |
(i<<COMMON_TIMEOUT_IDX_SHIFT);
}
}
/* Now remember what the new time turned out to be. */
base->event_tv = *tv;
}