Libevent源码分析--- 时间管理

Posted 子曰帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Libevent源码分析--- 时间管理相关的知识,希望对你有一定的参考价值。

时间获取

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;

小根堆

common_list

以上是关于Libevent源码分析--- 时间管理的主要内容,如果未能解决你的问题,请参考以下文章

Libevent源码分析 hello-world

Libevent源码分析--- libevent事件机制

Libevent源码分析--- libevent事件机制

Libevent源码分析--- bufferevent

libevent源码分析--(转)

Libevent源码分析系列