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源码分析(二)--- 时间管理)