libevent不只支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件。后端
首先,event_base中有一个成员struct min_heap timeheap,这是一个最小堆,用来存储timeout事件的结构之一。先来看一下它的定义:数组
typedef struct min_heap { struct event** p; size_t n, a; } min_heap_t;
p是一个能够动态扩展的指针数组,数组长度为a,n表示堆成员数量,堆成员是指向event结构的指针。libevent中最小堆相关的操做定义在minheap-internal.h源码文件中,使用这些操做能够维持数组中的成员保持最小堆性质。函数
堆排序的依据是event结构中的成员ev_timeout,这个成员记录了事件超时的绝对时间(即超时时间点)。根据最小堆性质,堆顶元素老是最先超时的事件。oop
使用min_heap的堆顶元素,libevent老是能够知道从如今起到有事件超时所需的时间tv,这个操做在event_base_loop中调用timeout_next函数完成。libevent处理io事件的后端接口,如select,都有一个时间参数做为阻塞的最长时间,将tv做为后端接口的入参,那么event_base_loop在循环中老是会在有事件超时以前被唤醒。唤醒后再循环检查min_heap的堆顶元素是否超时,超时则将它加入到event_base的待执行回调函数链表,这步操做在event_base_loop中调用timeout_process函数实现。如此便可实现超时事件的响应。this
仅使用min_heap成员,event_base已经足够管理timeout事件了,可是当有太多的timeou事件时,维持最小堆性质会成为一个耗时的操做。为解决这个问题,libevent使用了一个叫作common_timeout_list的结构,将超时时间相近的事件放到一块儿,这些事件在min_heap中仅留一个表明,减小min_heap的成员数量。common_timeout_list结构的定义以下(源码在event-internal.h中):spa
struct common_timeout_list { /* List of events currently waiting in the queue. */ struct event_list events; /* 'magic' timeval used to indicate the duration of events in this * queue. */ struct timeval duration; /* Event that triggers whenever one of the events in the queue is * ready to activate */ struct event timeout_event; /* The event_base that this timeout list is part of */ struct event_base *base; };
events是一个事件的双向链表,链接了全部属于这个common_timeout_list的超时事件,struct event结构中的成员ev_timeout_pos.ev_next_with_common_timeout正是用来构造这个链表;duration是一个带有特殊标记的时间戳(后面解释如何标记);timeout_event便是加入min_heap中的表明事件;base记录了归属的event_base结构。设计
event_base中能够存在多个common_timeout_lsit结构,它使用common_timeout_queues进行管理,这是一个动态扩展的指针数组,每个成员都指向一个common_timeout_list结构,n_common_timeouts_allocated表示动态数组的长度,n_common_timeouts表明有效的成员数目,common_timeout_queues的结构能够用图3-1表示:指针
图3-1 common_timeout_queues结构code
相似timeout_event这种特殊事件被称为内部事件,相似的设计在libevent处理signal事件中也有用到。blog
common_timeout_list事件中timeval的特殊标记:
libevent设计了以下标记:
/** Mask used to get the real tv_usec value from a common timeout. */ #define COMMON_TIMEOUT_MICROSECONDS_MASK 0x000fffff #define MICROSECONDS_MASK COMMON_TIMEOUT_MICROSECONDS_MASK #define COMMON_TIMEOUT_IDX_MASK 0x0ff00000 #define COMMON_TIMEOUT_IDX_SHIFT 20 #define COMMON_TIMEOUT_MASK 0xf0000000 #define COMMON_TIMEOUT_MAGIC 0x50000000
全部common_timeout_list链表中的event的ev_timeout.tv_usec都会异或上值COMMON_TIMEOUT_MAGIC做为标识;并将这个common_timeout_lsit在common_timeout_queues数组中的索引记录在第21bit至28bit中;真正的tv_usec值只使用了COMMON_TIMEOUT_MICROSECONDS_MASK标记的比特位。