libevent源碼分析二--timeout事件響應

  libevent不只支持io事件,同時還支持timeout事件與signal事件,這篇文件將分析libevent是如何組織timeout事件以及如何響應timeout事件。後端

1.  min_heap

  首先,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

2.  event_base_loop

使用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

3.  common_timeout_queues

  僅使用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

  •   首先,common_timeout_list中的事件是按超時時間順序排列的
  •   而後,common_timout_lsit中有一個專職的事件timeout_event用來加入到min_heap中,它的超時時間是事件鏈表中最先的超時時間。timeout_event的回調函數也是特殊設計的,做用是遍歷它歸屬的common_timeout_list上的事件鏈表,將超時的事件加入到event_base的待執行回調函數鏈表上。timeout_event的優先級被設置爲0,在event_base_loop處理待執行回調函數時會優先執行,在它的回調函數中處理的超時事件加入的待執行回調函數鏈表優先級較低,所以這些事件的回調函數還能夠在同一次event_base_loop循環中被執行。

相似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標記的比特位。

相關文章
相關標籤/搜索