libevent-timeout-minheap(超时管理、最小堆)

libevent版本号:2.1.8-stable

 

libevent是一个优秀的开源库,我曾尝试过至少4次逐行理解代码,但都以失败告终,代码量还是有一些的(其实是水平有限23333),有一个说法---人能理解的代码上限是一万行,不在乎真假,我先采取逐功能阅读的方式来理解这个库的代码以及逻辑。

本文主要记录libevent的超时机制是如何利用minheap实现的,minheap的实现不在此展开,但是minheap的一些操作函数比较重要会做一些介绍。

关于最小堆的一篇不错的文章:https://www.cnblogs.com/MOBIN/p/5374217.html

 

一.minheap数据结构以及基本操作函数

最小堆结构体(minheap-internal.h):

typedef struct min_heap
{
	struct event** p;//首地址
	unsigned n, a;//n:元素个数,a:元素容量
} min_heap_t;

 

一些基本操作(minheap-internal.h):

static inline void	     min_heap_ctor_(min_heap_t* s);//初始化堆,调用前需要malloc
static inline void	     min_heap_dtor_(min_heap_t* s);//删除堆,free
static inline void	     min_heap_elem_init_(struct event* e);//初始化超时事件,min_heap_idx = -1
static inline int	     min_heap_elt_is_top_(const struct event *e);//超时事件是否为顶
static inline int	     min_heap_empty_(min_heap_t* s);//堆是否为空
static inline unsigned	     min_heap_size_(min_heap_t* s);//获取当前事件个数,min_heap_t中的n
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_adjust_(min_heap_t *s, struct event* e);//调整e在最小堆的位置
static inline int	     min_heap_erase_(min_heap_t* s, struct event* e);//在最小堆中销毁e,并用堆的最后一个堆元素进行替换和调整
static inline void	     min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e);//将某个叶子结点的堆元素上浮到合适的位置,用于堆元素的插入
static inline void	     min_heap_shift_up_unconditional_(min_heap_t* s, unsigned hole_index, struct event* e);和min_heap_shift_up_类似,只是一次无条件上浮
static inline void	     min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e);//将某个叶子结点的堆元素下浮到合适的位置,用于堆元素的取出

二.timeout

下面是一个定时器例子:

// libevent头文件
#include <event.h>

#include <stdio.h>
#include <iostream>
using namespace std;

// 定时事件回调函数
void onTime(int sock, short event, void *arg)
{
    cout << "on time!" << endl;
    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;
 // 重新添加定时事件(定时事件触发后默认自动删除)
    event_add((struct event*)arg, &tv);
}

void test_timer()
{
   // 初始化
    event_init();
    struct event evTime;

   // 设置定时事件
    evtimer_set(&evTime, onTime, &evTime);
    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;

    // 添加定时事件
    event_add(&evTime, &tv);

   // 事件循环
    event_dispatch();
}

  排除与超时无关的功能和代码,我们来看一些libevent内部的代码。

  存放超时事件的结构体(event-internal.h):

struct event_base {
        ......
	/** Priority queue of events with timeouts. */
	struct min_heap timeheap;
        ......
};

初始化timeheap的函数过程:

event_init()/event_base_new() --> event_base_new_with_config(cfg) -->min_heap_ctor_(&base->timeheap)

反始化timeheap的函数过程:

event_base_free() --> event_base_free_(base, 1) --> min_heap_dtor_(&base->timeheap)

初始化事件元素的函数过程:

evtimer_set(ev, cb, arg)/event_set((ev), -1, 0, (cb), (arg)) --> event_assign(...) --> min_heap_elem_init_(ev)

timeheap push事件元素的函数过程:

event_add(ev, tv) --> event_add_nolock_(ev, tv, 0)-->event_queue_insert_timeout(base, ev)-->min_heap_push_(&base->timeheap, ev)

timeheap pop堆顶事件元素的函数过程:

event_dispatch() --> event_loop(0) --> event_base_loop(current_base, 0) --> timeout_process(base) --> min_heap_top_(&base->timeheap)

三.事件处理 event_base_loop :

int
event_base_loop(struct event_base *base, int flags)
{
	struct timeval tv;
	struct timeval *tv_p;
        int res, done, retval = 0;

        ......
        
        base->running_loop = 1;
	clear_time_cache(base);//清除时间缓存,好让下面的gettime获得当前系统时间而不是获得缓存
的时间
        done = 0;
        base->event_gotterm = base->event_break = 0;
        ......
        while (!done) {//进入事件主循环
    		//这两个是用来中断循环的标识
		if (base->event_gotterm) {//event_loopexit_cb()可设置
			break;
		}

		if (base->event_break) { //event_base_loopbreak()可设置
			break;
		}
        
		tv_p = &tv; //校准时间
		if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {//如果激活事件数量为0 &&
			timeout_next(base, &tv_p);//获取堆顶事件
		} else {
			/*
			 * if we have active events, we just poll new events
			 * without waiting.
			 */
			evutil_timerclear(&tv);//有就绪事件则无需等待epoll_wait,时间设置为0
		}

                ......

		event_queue_make_later_events_active(base);
		//清理时间缓存
		clear_time_cache(base);
		// 调用模型的epoll_wait/select/poll 等tv_p是刚才计算的最小时间间隔
		res = evsel->dispatch(base, tv_p);
                 //更新base中的时间, 下面就是调用定时事件和IO事件。
		update_time_cache(base);
		//判断定时器事件是否发生了,若发生就将事件加入激活队列
		timeout_process(base);

		if (N_ACTIVE_CALLBACKS(base)) {//有激活事件
			int n = event_process_active(base);//按优先级从高到低处理激活事件,回调函数
			if ((flags & EVLOOP_ONCE)//执行完活跃事件的回调就退出。阻塞
			    && N_ACTIVE_CALLBACKS(base) == 0
			    && n != 0)
				done = 1;
		} else if (flags & EVLOOP_NONBLOCK)//没有激活事件,不阻塞,退出
			done = 1;
	}
        
        .......

        }
}

 

 

 

 

 

参考:

libevent源码分析(9)--2.1.8--事件注册和删除   https://blog.youkuaiyun.com/beitiandijun/article/details/72834786

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值