您的位置:首页 > 编程语言 > C语言/C++

基于Libevent最小根堆定时器的C++定时器实现

2016-01-14 17:51 549 查看
在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 */
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: