1. 程式人生 > >基於Libevent最小根堆定時器的C++定時器實現

基於Libevent最小根堆定時器的C++定時器實現

在libevent中定時器的實現是通過基於最小堆的優先順序佇列來實現的,對於這兩個資料結構比較陌生的可以去翻演算法導論的6.5節中,主要的原始碼都在min_heap.c中,下面是C++的實現。

資料結構

typedef struct min_heap
{
  struct event** p;
  unsigned n, a;
} min_heap_t;
在這個資料結構中 p也就是整個優先順序佇列,而這個優先順序佇列的每個節點都是一個struct *event.n表示這個佇列的元素個數。a表示這個佇列的大小。
#ifndef VOS_TIMER_H_  
#define VOS_TIMER_H_  
  
#define RELATIVE_TIMER 1  
#define ABSOLUTE_TIMER 2  
  
namespace vos  
{  
  
struct event;  
typedef struct min_heap  
{  
    struct event** p;  
    unsigned n, a;  
} min_heap_t;  
  
class Timer  
{  
public:  
    Timer();  
    virtual ~Timer();  
    /************************************** 
     * input: interval: 每次執行的時間隔間, 單位是毫秒。 
     *        fun arg : 回撥函式以及引數。 
     *        flag    : 絕對定時器還是相對定時器,如果是相對定時器 
     *        exe_num : 只有在相對定時器才有效,表示執行的次數。最少為1次 
     * return: 生成定時器的ID 
     **************************************/  
    unsigned int timer_add(int interval, void (*fun)(void*), void *arg,  int flag = ABSOLUTE_TIMER,  
            int exe_num = 0);  
    /*************************************** 
     * description: 
     * 去掉已經加入的定時器,比如產生定時器的母體已經消亡了,在消亡之間要將其刪除。 
     * 相對定時器在任務完成後會Timer會自己釋放掉。 
     ***************************************/  
    bool timer_remove(unsigned int timer_id);  
    /*************************************** 
     * description: Timer屬於被動物件,沒有自己的執行執行緒,屬於被呼叫者。這樣主要是為了避免產生執行緒同步。 
     * 定時器的迴圈處理函式,由定時器的擁有者進行迴圈呼叫。它的最小時間間隔決定了定時器的精度。 
     ***************************************/  
    int timer_process();  
  
private:  
    struct min_heap _min_heap;  
    unsigned int _timer_id;  
};  
  
} /* namespace vos */  
#endif /* VOS_TIMER_H_ */

#include "vos_timer.h"  
  
#ifndef __WINDOWS  
#include <sys/time.h>  
#else  
#include <windows.h>  
#endif  
  
#include <time.h>  
#include <stdio.h>  
#include <stdlib.h>  
  
#define evutil_timercmp(tvp, uvp, cmp)                          \  
    (((tvp)->tv_sec == (uvp)->tv_sec) ?                           \  
    ((tvp)->tv_usec cmp (uvp)->tv_usec) :                     \  
    ((tvp)->tv_sec cmp (uvp)->tv_sec))  
  
#define evutil_timersub(tvp, uvp, vvp)                      \  
    do {                                                    \  
    (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;     \  
    (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;  \  
    if ((vvp)->tv_usec < 0) {                         \  
    (vvp)->tv_sec--;                             \  
    (vvp)->tv_usec += 1000000;                       \  
    }                                                   \  
    } while (0)  
  
#define evutil_timeradd(tvp, uvp, vvp)                          \  
    do {                                                        \  
    (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;         \  
    (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;       \  
    if ((vvp)->tv_usec >= 1000000) {                      \  
    (vvp)->tv_sec++;                                 \  
    (vvp)->tv_usec -= 1000000;                           \  
    }                                                       \  
    } while (0)  
  
#ifdef __WINDOWS  
int gettimeofday(struct timeval* tv, void * attr)  
{  
    union  
    {  
        long long ns100;  
        FILETIME ft;  
    }now;  
  
    GetSystemTimeAsFileTime (&now.ft);  
    tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL);  
    tv->tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL);  
    return (0);  
}  
#endif  
  
namespace vos  
{  
struct event  
{  
    unsigned int min_heap_idx; /* for managing timeouts */  
    unsigned int timer_id;  
    struct timeval ev_interval;  
    struct timeval ev_timeout;  
    int ev_exe_num;  
  
    void (*ev_callback)(void *arg);  
    void *ev_arg;  
  
    int ev_res; /* result passed to event callback */  
    int ev_flags;  
};  
  
/***建構函式  ***************/  
static inline void min_heap_ctor(min_heap_t* s);  
/***解構函式  ***************/  
static inline void min_heap_dtor(min_heap_t* s);  
/***初始化函式  ***************/  
static inline void min_heap_elem_init(struct event* e);  
/****比較函式***************/  
static inline int min_heap_elem_greater(struct event *a, struct event *b);  
  
static inline int min_heap_empty(min_heap_t* s);  
  
static inline unsigned min_heap_size(min_heap_t* s);  
/****返回棧頂元素************/  
static inline struct event* min_heap_top(min_heap_t* s);  
/****調整定時器的大小*********/  
static inline int min_heap_reserve(min_heap_t* s, unsigned n);  
/****放入資料*************/  
static inline int min_heap_push(min_heap_t* s, struct event* e);  
/****取得最後上面的資料******/  
static inline struct event* min_heap_pop(min_heap_t* s);  
/****消除一個定時器元素*******/  
static inline int min_heap_erase(min_heap_t* s, struct event* e);  
/****向上調整 ************/  
static inline void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e);  
/****向下調整************/  
static inline void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e);  
  
static inline void gettime(struct timeval *tm);  
  
Timer::Timer() :  
        _timer_id(0)  
{  
    min_heap_ctor(&_min_heap);  
}  
  
Timer::~Timer()  
{  
    for (int i = 0; i < _min_heap.n; i++)  
    {  
        free(_min_heap.p[i]);  
    }  
    min_heap_dtor(&_min_heap);  
}  
  
unsigned int Timer::timer_add(int interval, void(*fun)(void*), void *arg,  
        int flag /* = ABSOLUTE_TIMER */, int exe_num /* =  0 */)  
{  
    struct event * ev = (struct event*) malloc(sizeof(struct event));  
    min_heap_elem_init(ev);  
    if (NULL == ev)  
        return NULL;  
    struct timeval now;  
    gettime(&now);  
    ev->ev_interval.tv_sec = interval / 1000;  
    ev->ev_interval.tv_usec = (interval % 1000) * 1000;  
    evutil_timeradd(&now, &(ev->ev_interval), &(ev->ev_timeout));  
    ev->ev_flags = flag;  
    ev->ev_callback = fun;  
    ev->ev_arg = arg;  
    ev->ev_exe_num = exe_num;  
    ev->timer_id = _timer_id++;  
  
    min_heap_push(&_min_heap, ev);  
  
    return ev->timer_id;  
}  
  
bool Timer::timer_remove(unsigned int timer_id)  
{  
    for (int i = 0; i < _min_heap.n; i++)  
    {  
        if (timer_id == _min_heap.p[i]->timer_id)  
        {  
            struct event * e = _min_heap.p[i];  
            min_heap_erase(&_min_heap, _min_heap.p[i]);  
            free(e);  
            return true;  
        }  
    }  
    return false;  
}  
  
int Timer::timer_process()  
{  
    struct event *event;  
    struct timeval now;  
    while ((event = min_heap_top(&_min_heap)) != NULL)  
    {  
        gettime(&now);  
        if (evutil_timercmp(&now, &(event->ev_timeout), < ))  
            break;  
        min_heap_pop(&_min_heap);  
        event->ev_callback(event->ev_arg);  
        if (event->ev_flags == ABSOLUTE_TIMER  
                || (event->ev_flags == RELATIVE_TIMER && --event->ev_exe_num > 0))  
        {  
            evutil_timeradd(&(event->ev_timeout), &(event->ev_interval), &(event->ev_timeout));  
            min_heap_push(&_min_heap, event);  
        }  
        else  
        {  
            free(event);  
        }  
    }  
  
    return 0;  
}  
  
void gettime(struct timeval *tm)  
{  
    gettimeofday(tm, NULL);  
}  
  
int min_heap_elem_greater(struct event *a, struct event *b)  
{  
    return evutil_timercmp(&a->ev_timeout, &b->ev_timeout, >);  
}  
  
void min_heap_ctor(min_heap_t* s)  
{  
    s->p = 0;  
    s->n = 0;  
    s->a = 0;  
}  
  
void min_heap_dtor(min_heap_t* s)  
{  
    if (s->p)  
        free(s->p);  
}  
  
void min_heap_elem_init(struct event* e)  
{  
    e->min_heap_idx = -1;  
}  
  
int min_heap_empty(min_heap_t* s)  
{  
    return 0u == s->n;  
}  
  
unsigned min_heap_size(min_heap_t* s)  
{  
    return s->n;  
}  
  
struct event* min_heap_top(min_heap_t* s)  
{  
    return s->n ? *s->p : 0;  
}  
  
int min_heap_push(min_heap_t* s, struct event* e)  
{  
    if (min_heap_reserve(s, s->n + 1))  
        return -1;  
    min_heap_shift_up_(s, s->n++, e);  
    return 0;  
}  
  
struct event* min_heap_pop(min_heap_t* s)  
{  
    if (s->n)  
    {  
        struct event* e = *s->p;  
        min_heap_shift_down_(s, 0u, s->p[--s->n]);  
        e->min_heap_idx = -1;  
        return e;  
    }  
    return 0;  
}  
  
int min_heap_erase(min_heap_t* s, struct event* e)  
{  
    if (((unsigned int) -1) != e->min_heap_idx)  
    {  
        struct event *last = s->p[--s->n];  
        unsigned parent = (e->min_heap_idx - 1) / 2;  
        /* we replace e with the last element in the heap.  We might need to 
         shift it upward if it is less than its parent, or downward if it is 
         greater than one or both its children. Since the children are known 
         to be less than the parent, it can't need to shift both up and 
         down. */  
        if (e->min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last))  
            min_heap_shift_up_(s, e->min_heap_idx, last);  
        else  
            min_heap_shift_down_(s, e->min_heap_idx, last);  
        e->min_heap_idx = -1;  
        return 0;  
    }  
    return -1;  
}  
  
int min_heap_reserve(min_heap_t* s, unsigned n)  
{  
    if (s->a < n)  
    {  
        struct event** p;  
        unsigned a = s->a ? s->a * 2 : 8;  
        if (a < n)  
            a = n;  
        if (!(p = (struct event**) realloc(s->p, a * sizeof *p)))  
            return -1;  
        s->p = p;  
        s->a = a;  
    }  
    return 0;  
}  
  
void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e)  
{  
    unsigned parent = (hole_index - 1) / 2;  
    while (hole_index && min_heap_elem_greater(s->p[parent], e))  
    {  
        (s->p[hole_index] = s->p[parent])->min_heap_idx = hole_index;  
        hole_index = parent;  
        parent = (hole_index - 1) / 2;  
    }  
    (s->p[hole_index] = e)->min_heap_idx = hole_index;  
}  
  
void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e)  
{  
    unsigned min_child = 2 * (hole_index + 1);  
    while (min_child <= s->n)  
    {  
        min_child -= min_child == s->n  
                || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]);  
        if (!(min_heap_elem_greater(e, s->p[min_child])))  
            break;  
        (s->p[hole_index] = s->p[min_child])->min_heap_idx = hole_index;  
        hole_index = min_child;  
        min_child = 2 * (hole_index + 1);  
    }  
    min_heap_shift_up_(s, hole_index, e);  
}  
} /* namespace vos */