Libevents的基本操做單元是event,每個event表明了一些條件的集合,這些條件包括:html
文件描述符已經準備好讀或寫算法
文件描述符正在變爲就緒,準備好讀或寫(僅限於邊沿觸發)後端
超時事件緩存
信號發生安全
用戶觸發事件app
events都有相似的生命週期。一旦調用Libevent函數建立好event,並將其關聯到一個event_base以後,他就是「已初始化」狀態(initialized)。這種狀態下,能夠進行add操做,將其狀態變爲base中的「掛起」狀態(pending),處於「掛起」狀態的event,若是觸發事件的條件發生了(好比,文件描述符的狀態發生變化,或者超時了),那麼event的狀態變爲「激活」狀態(active),而後它的回調函數(用戶提供)開始運行。若是該event配置了「持久」屬性(persistent),那麼它的狀態依然保持爲「掛起」,不然,在回調函數運行時,它的狀態就再也不是「掛起」(「非掛起」狀態)。能夠經過delete操做,將一個「掛起」狀態的event變爲「非掛起」狀態(non-pending),或者經過add操做,將「非掛起」的event變爲「掛起」狀態。less
一:構建event對象socket
建立新的event,可使用event_new接口:函數
#define EV_TIMEOUT 0x01oop
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
typedef void (*event_callback_fn)(evutil_socket_t, short, void*);
struct event * event_new(struct event_base *base, evutil_socket_t fd,
shortwhat, event_callback_fn cb,
void* arg);
void event_free(struct event * event);
event_new函數分配而且建立一個新的event對象,並與base進行關聯。what參數是上面列出標誌的集合,它們的具體意義見下方。若是fd是非負的整數,則它表明了咱們須要觀察可讀或可寫事件的文件。當event變爲激活時,Libevent就會調用回調函數cb,將文件描述符參數fd,全部觸發事件的標誌位域,以及event_new的最後一個參數:arg傳遞個cb。
若是發生了內部錯誤,或者參數非法,則event_new返回NULL。
全部新的events都是「已初始化」和「非掛起」狀態,能夠調用event_add函數將這樣的event變爲「掛起」狀態。
調用event_free能夠銷燬event。對「掛起」或「激活」狀態的event調用event_free也是安全的:在銷燬它以前,會將其變爲「非掛起」以及「非激活」狀態。
#include <event2/event.h>
void cb_func(evutil_socket_t fd, short what, void * arg)
{
const char *data = arg;
printf("Got an event on socket %d:%s%s%s%s [%s]",
(int) fd,
(what&EV_TIMEOUT) ? " timeout" : "",
(what&EV_READ) ? " read" : "",
(what&EV_WRITE) ? " write" : "",
(what&EV_SIGNAL) ? " signal" : "",
data);
}
void main_loop(evutil_socket_t fd1, evutil_socket_t fd2)
{
struct event *ev1, *ev2;
struct timeval five_seconds = {5,0};
struct event_base * base = event_base_new();
/* The caller has already set up fd1,fd2 somehow, and make them
nonblocking. */
ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,
(char*)"Reading event");
ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,
(char*)"Writing event");
event_add(ev1, &five_seconds);
event_add(ev2, NULL);
event_base_dispatch(base);
}
上述函數在<event2/event.h>文件中定義。
event標誌:
EV_TIMEOUT:
該標誌代表,超時時間事後,該event變爲「激活」狀態。(注意:在構建event時,EV_TIMEOUT標誌是被忽略的:當add event時能夠設置超時時間,也能夠不設置。當超時發生時,回調函數的what參數將會設置該標誌。)
EV_READ
該標誌代表,當文件描述符準備好讀時,event將會變爲「激活」
EV_WRITE
該標誌代表,當文件描述符準備好寫時,event將會變爲「激活」
EV_SIGNAL
用來實現信號探測,參見下面的「構造信號事件」
EV_PERSIST:
標誌該event具備「持久」屬性,參見下面的「事件持久性」
EV_ET:
指明若是event_base的底層方法支持「邊沿觸發」的話,那麼該event應該是邊沿觸發的。這將會影響到EV_READ和EV_WRITE
自Libevent2.0.1-alpha版本以來,同一時刻,針對同一個文件描述符,能夠有任意數量的event在一樣的條件上「掛起」。好比,當給定的fd變爲可讀時,可使兩個events都變爲激活狀態。可是他們的回調函數的調用順序是未定義的。
全部這些標誌都在<event2/event.h>中定義。
二:事件持久性
默認狀況下,當一個「掛起」的event變爲「激活」時(要麼是由於fd準備好讀或寫,要麼是超時時間到),那麼在它的回調函數執行以前,它就會變爲「非掛起」狀態。所以,若是但願再次使event變爲「掛起」狀態,能夠在回調函數內部再次調用event_add函數。
若是event設置了EV_PERSIST標誌,那麼event就是「持久」的。這意味着event在回調函數激活的時候,依然保持「掛起」狀態。若是但願在回調函數中將event變爲「非掛起」狀態,則能夠調用event_del函數。
當event的回調函數運行時,「持久」event的超時時間就會被重置。所以,若是某個event標誌爲EV_READ|EV_PERSIST,而且將超時時間設置爲5秒,則該event在下面的條件發生時,會變爲「激活」:
當該socket準備好讀時;
距離上次event變爲激活狀態後,又過了5秒鐘。
三:建立一個能夠將自身做爲回調函數參數的的event
常常可能會但願建立這樣一個event,它自己就是是回調函數的參數之一。不能僅僅傳遞一個指向event的指針做爲event_new的參數,由於彼時它尚未建立。此時,能夠經過調用event_self_cbarg函數解決這樣的問題。
void*event_self_cbarg();
該函數返回一個「魔術」指針,使得event_new建立一個自己就能做爲回調函數參數的event。
#include <event2/event.h>
static int n_calls = 0;
void cb_func(evutil_socket_t fd, short what, void * arg)
{
struct event *me = arg;
printf("cb_func called %d times so far.\n", ++n_calls);
if (n_calls > 100)
event_del(me);
}
void run(struct event_base * base)
{
struct timeval one_sec = { 1, 0 };
struct event *ev;
/* We're going to set up a repeating timerto get called 100 times. */
ev = event_new(base, -1, EV_PERSIST, cb_func, event_self_cbarg());
event_add(ev, &one_sec);
event_base_dispatch(base);
}
該函數還能夠與函數event_new,evtimer_new, evsignal_new, event_assign, evtimer_assign和evsignal_assign一塊兒使用。然而對於非event來講,他不會做爲回調函數的參數。
四:純超時events
方便起見,Libevent提供了一系列以evtimer_開頭的宏,這些宏能夠代替event_*函數,來分配和操做純超時events。使用這些宏僅能提升代碼的清晰度而已。
#define evtimer_new(base, callback, arg) event_new((base), -1, 0, (callback), (arg))
#define evtimer_add(ev, tv) event_add((ev),(tv))
#define evtimer_del(ev) event_del(ev)
#define evtimer_pending(ev, tv_out) event_pending((ev), EV_TIMEOUT, (tv_out))
五:構造信號事件
Libevent也能夠監控POSIX類的信號。構建一個信號處理函數,可使用下面的接口:
#define evsignal_new(base, signum, cb, arg)\
event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)
除了提供一個表明信號值的整數,而不是一個文件描述符以外。它的參數與event_new是同樣的。
struct event * hup_event;
struct event_base *base = event_base_new();
/*call sighup_function on a HUP signal */
hup_event= evsignal_new(base, SIGHUP, sighup_function, NULL);
注意:信號回調函數是在信號發生以後,在eventloop中調用的。因此,它們能夠調用那些,對於普通POSIX信號處理函數來講不是信號安全的函數。
注意:不要在一個信號event上設置超時,不支持這樣作。
對於信號event,一樣有一些方便的宏可使用:
#define evsignal_add(ev, tv) event_add((ev), (tv))
#define evsignal_del(ev) event_del(ev)
#define evsignal_pending(ev, what, tv_out) event_pending((ev), (what), (tv_out))
警告:當前版本的Libevent,對於大多數的後端方法來講,同一時間,每一個進程僅能有一個event_base能夠用來監聽信號。若是一次向兩個event_base添加event,即便是不一樣的信號,也僅僅會只有一個event_base能夠接收到信號。對於kqueue來講,不存在這樣的限制。
六:不在堆中分配event
出於性能或者其餘緣由的考慮,一些人喜歡將event做爲一個大的結構體的一部分進行分配。對於這樣的event,它節省了:
內存分配器在堆上分配小對象的開銷;
event指針的解引用的時間開銷;
若是event沒有在緩存中,緩存不命中的時間開銷。
這種方法的風險在於,與其餘版本的Libevent之間不知足二進制兼容性,他們可能具備不一樣的event大小。
這些開銷都很是小,對於大多數應用來講是可有可無的。除非肯定知道,應用程序由於使用堆分配的event而存在嚴重的性能損失,不然應該堅持實用event_new。若是後續版本的Libevent使用比當前Libevent更大的event結構,那麼使用event_assign有可能會致使難以診斷的錯誤。
int event_assign(struct event * event, struct event_base * base,
evutil_socket_t fd, short what,
void(*callback)(evutil_socket_t, short, void *), void * arg);
event_assign的參數與event_new相同,除了event參數,該參數指針必須指向一個未初始化的event。該函數成功時返回0,失敗時返回-1.
#include <event2/event.h>
/*Watch out! Including event_struct.h means that your code willnot
* be binary-compatible with future versions ofLibevent. */
#include <event2/event_struct.h>
#include <stdlib.h>
struct event_pair {
evutil_socket_t fd;
struct event read_event;
struct event write_event;
};
void readcb(evutil_socket_t, short, void*);
void writecb(evutil_socket_t, short, void*);
struct event_pair * event_pair_new(struct event_base * base, evutil_socket_t fd)
{
struct event_pair *p = malloc(sizeof(struct event_pair));
if (!p) return NULL;
p->fd = fd;
event_assign(&p->read_event, base, fd, EV_READ|EV_PERSIST, readcb, p);
event_assign(&p->write_event, base, fd, EV_WRITE|EV_PERSIST,writecb, p);
return p;
}
一樣可使用event_assign來初始化棧或者靜態存儲區中的events。
警告:
對於已經在event_base中處於「掛起」狀態的event,永遠不要調用event_assign。這樣作會致使極爲難以診斷的錯誤。若是event已經初始化,而且處於「掛起」狀態,那麼在調用event_assign以前應該先調用event_del。
對於使用event_assign分配的純超時event或者信號event,一樣有方便的宏可使用:
#define evtimer_assign(event, base, callback, arg) \
event_assign(event, base, -1, 0, callback, arg)
#define evsignal_assign(event, base, signum, callback, arg) \
event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg)
若是須要在與將來版本的LIbevent保持二進制兼容性的同時,使用event_assign,能夠調用Libevent中的函數,獲得運行時的event結構大小:
size_t event_get_struct_event_size(void);
該函數返回須要爲event結構預留的字節數。再次提醒,只有在肯定堆分配致使很明顯的性能問題時,才應該使用該函數,由於它使你的代碼難讀又難寫。
注意,未來版本的event_get_struct_event_size()的返回值可能比sizeof(structevent)小,這意味着event結構的末尾的額外字節僅僅是保留用於將來版本的Libevent的填充字節。
下面是一個使用event_get_struct_size的例子:
#include <event2/event.h>
#include <stdlib.h>
/*When we allocate an event_pair in memory, we'll actually allocate
* more space at the end of the structure. We define some macros
* to make accessing those events lesserror-prone. */
struct event_pair {
evutil_socket_t fd;
};
/*Macro: yield the struct event 'offset' bytes from the start of 'p' */
#define EVENT_AT_OFFSET(p, offset) \
((struct event*) ( ((char*)(p)) + (offset) ))
/*Macro: yield the read event of an event_pair */
#define READEV_PTR(pair) \
EVENT_AT_OFFSET((pair), sizeof(struct event_pair))
/*Macro: yield the write event of an event_pair */
#define WRITEEV_PTR(pair) \
EVENT_AT_OFFSET((pair), \
sizeof(struct event_pair)+event_get_struct_event_size())
/*Macro: yield the actual size to allocate for an event_pair */
#defineEVENT_PAIR_SIZE() \
(sizeof(struct event_pair)+2*event_get_struct_event_size())
voidreadcb(evutil_socket_t, short, void *);
voidwritecb(evutil_socket_t, short, void *);
struct event_pair *event_pair_new(struct event_base *base, evutil_socket_t fd)
{
struct event_pair *p = malloc(EVENT_PAIR_SIZE());
if (!p) return NULL;
p->fd = fd;
event_assign(READEV_PTR(p), base, fd,EV_READ|EV_PERSIST, readcb, p);
event_assign(WRITEEV_PTR(p), base, fd,EV_WRITE|EV_PERSIST, writecb, p);
return p;
}
event_assign函數定義在文件<event2/event.h>中。event結構體定義在<event2/event_struct.h>文件中。
七:將events置爲「掛起」或者「非掛起」
剛建立的一個event,實際上不能作任何事,直到經過調用event_add進行adding操做,將其置爲「掛起」狀態。
int event_add(struct event *ev, const struct timeval *tv);
在「非掛起」狀態的events上執行event_add操做,則會使得該event在配置的event_base上變爲「掛起」狀態。該函數返回0表示成功,返回-1表示失敗。若是tv爲NULL,則該event沒有超時時間。不然,tv以秒和毫妙表示超時時間。
若是在已是「掛起」狀態的event進行event_add操做,則會保持其「掛起」狀態,而且會重置其超時時間。若是event已是「掛起」狀態,並且以NULL爲超時時間對其進行re-add操做,則event_add沒有任何做用。
注意:不要設置tv爲但願超時事件執行的時間,好比若是置tv->tv_sec=time(NULL)+10,而且當前時間爲2010/01/01,則超時時間爲40年以後,而不是10秒以後。
int event_del(struct event *ev);
在已經初始化狀態的event上調用event_del,則會將其狀態變爲「非掛起」以及「非激活」狀態。若是event的當前狀態不是「掛起」或「激活」狀態,則該函數沒有任何做用。該函數返回0表示成功,返回-1表示失敗。
注意,若是在event剛變爲「激活」狀態,可是它的回調函數尚未執行時,調用event_del函數,則該操做使得它的回調函數不會執行。
int event_remove_timer(struct event *ev);
最後,能夠在不刪除event上的IO事件或信號事件的狀況下,刪除一個「掛起」狀態的event上的超時事件。若是該event沒有超時事件,則event_remove_timer沒有做用。若是event沒有IO事件或信號事件,只有超時事件的話,則event_remove_timer等同於event_del。該函數返回0表示成功,-1表示失敗。
這些函數都是在文件<event2/event.h>中定義的。
八:事件的優先級
當多個事件在同一時間觸發時,Libevent對於他們回調函數的調用順序是沒有定義的。能夠經過優先級,定義某些「更重要」的events。
每個event_base都有一個或多個優先級的值。在event初始化以後,添加到event_base以前,能夠設置該event的優先級。
int event_priority_set(struct event *event, int priority);
event的優先級數必須是位於0到「event_base優先級」-1這個區間內。該函數返回0表示成功,返回-1表示失敗。
當具備多種優先級的多個events同時激活的時候,低優先級的events不會運行。Libevent會只運行高優先級的events,而後從新檢查events。只有當沒有高優先級的events激活時,纔會運行低優先級的events。
#include <event2/event.h>
void read_cb(evutil_socket_t, short, void*);
void write_cb(evutil_socket_t, short, void*);
voidmain_loop(evutil_socket_t fd)
{
struct event *important, *unimportant;
struct event_base *base;
base = event_base_new();
event_base_priority_init(base, 2);
/* Now base has priority 0, and priority 1 */
important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL);
unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL);
event_priority_set(important, 0);
event_priority_set(unimportant, 1);
/*Now, whenever the fd is ready for writing, the write callback will
happen before the read callback. The read callback won't happen at
all until the write callback is no longeractive.*/
}
若是沒有設置一個event的優先級,則它的默認優先級是「event_base隊列長度」除以2。該函數在文件<event2/event.h>中聲明。
九:檢查event狀態
有時可能但願知道event是否已經添加了(處於「掛起」狀態),或者檢查他關聯到哪一個event_base等。
int event_pending(const struct event *ev, short what, struct timeval *tv_out);
#define event_get_signal(ev) /* ... */
evutil_socket_t event_get_fd(const struct event *ev);
struct event_base *event_get_base(const struct event *ev);
short event_get_events(const struct event *ev);
event_callback_fn event_get_callback(const struct event *ev);
void*event_get_callback_arg(const struct event *ev);
int event_get_priority(const struct event *ev);
void event_get_assignment(const struct event*event,
struct event_base **base_out,
evutil_socket_t *fd_out,
short *events_out,
event_callback_fn *callback_out,
void **arg_out);
event_pending函數檢查給定的event是否處於「掛起」或「激活」狀態。若是確實如此,而且在what參數中設置了任何EV_READ, EV_WRITE, EV_SIGNAL或EV_TIMEOUT標誌的話,則該函數返回全部該event當前正在「掛起」或「激活」的標誌。
若是提供了tv_out參數,且在what參數中設置了EV_TIMEOUT參數,而且當前event確實在超時事件上「掛起」或者「激活」,則tv_out就會設置爲event的超時時間。
event_get_fd和event_get_signal函數返回event上配置的文件描述符或者信號值。event_get_base()返回其配置的event_base。event_get_events() 返回event上配置的事件標誌(EV_READ,EV_WRITE等)。event_get_callback函數和event_get_callback_arg函數返回event的回調函數和參數指針。event_get_priority函數返回event的當前優先級。
event_get_assignment函數在提供的參數指針中返回event的全部成分,若是參數指針爲NULL,則該成分被忽略。
#include <event2/event.h>
#include <stdio.h>
/*Change the callback and callback_arg of 'ev', which must not be pending. */
int replace_callback(struct event *ev, event_callback_fn new_callback,
void* new_callback_arg)
{
struct event_base *base;
evutil_socket_t fd;
short events;
int pending;
pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT, NULL);
if (pending) {
/*We want to catch this here so that we do notre-assign a
* pending event. That would be very very bad.*/
fprintf(stderr, "Error! replace_callbackcalled on a pending event!\n");
return -1;
}
event_get_assignment(ev, &base, &fd, &events,
NULL /* ignore oldcallback */ ,
NULL /* ignore oldcallback argument */);
event_assign(ev, base, fd, events,new_callback, new_callback_arg);
return 0;
}
這些函數在文件<event2/event.h>中定義。
十:找到當前正在運行的event
在調試程序時,能夠獲得當前正在運行的event的指針。
struct event * event_base_get_running_event(struct event_base * base);
注意,只有在base的loop中調用該函數,該函數纔有意義。在其餘線程調用時不支持的,並且會致使未定義的行爲。
該函數在<event2/event.h>中聲明。
十一:配置一次性的events
若是不須要對一個event進行屢次添加,或者對一個非持久的event,在add以後就會delete,則可使用event_base_once函數。
int event_base_once(struct event_base *, evutil_socket_t, short,
void (*) (evutil_socket_t, short, void*), void *, const struct timeval *);
該函數的參數與event_new同樣,不一樣的是它不支持EV_SIGNAL或EV_PERSIST標誌。獲得的內部event會以默認的優先級添加到event_base中並運行。當它的回調函數執行完成以後,Libevent將會釋放該內部event。該函數成功時返回0,失敗是返回-1.
經過event_base_once插入的event不能被刪除或者手動激活。若是但願能夠取消一個event,則須要經過常規的event_new或event_assign接口建立event。
注意,直到Libevent2.0以前,若是event一直沒有觸發,則它的內存永遠不會被釋放。從Libevent2.1.2-alpha版本開始,當event_base釋放時,即便events尚未被激活,它們的內存也會被釋放。可是依然要注意:若是它們的回調函數的參數具備關聯的內存,那麼除非程序中進行釋放,不然這些內存永遠不會被釋放。
十二:手動激活event
某些極少的狀況下,你可能但願在條件未被觸發的狀況下就激活event;
void event_active(struct event *ev, int what, short ncalls);
該接口使得event變爲「激活」狀態,激活標誌在what中傳入(EV_READ, EV_WRITE和EV_TIMEOUT的組合)。該event以前的狀態不必定非得要是「掛起」狀態,並且將其激活不會使其狀態變爲「掛起」狀態。
警告:在同一個event上遞歸調用event_active可能會致使資源耗盡。下面的例子就是不正確的示範:
structevent *ev;
static void cb(int sock, short which, void *arg) {
/* Whoops: Calling event_active on thesame event unconditionally
from within its callback means that no other eventsmight not get
run!*/
event_active(ev, EV_WRITE, 0);
}
int main(int argc, char**argv) {
struct event_base *base = event_base_new();
ev = event_new(base, -1, EV_PERSIST| EV_READ, cb, NULL);
event_add(ev, NULL);
event_active(ev, EV_WRITE, 0);
event_base_loop(base, 0);
return 0;
}
上面的例子描述了這樣的情形:event loop僅被執行一次,而cb會被無限的遞歸調用中。
Example:Alternative solution to the above problem using timers
struct event *ev;
struct timeval tv;
static void cb(int sock,short which, void *arg) {
if (!evtimer_pending(ev, NULL)) {
event_del(ev);
evtimer_add(ev, &tv);
}
}
int main(int argc, char**argv) {
struct event_base *base = event_base_new();
tv.tv_sec = 0;
tv.tv_usec = 0;
ev = evtimer_new(base, cb, NULL);
evtimer_add(ev, &tv);
event_base_loop(base, 0);
return 0;
}
Example:Alternative solution to the above problem usingevent_config_set_max_dispatch_interval()
structevent *ev;
static void cb(int sock, short which, void*arg) {
event_active(ev, EV_WRITE, 0);
}
intmain(int argc, char **argv) {
struct event_config *cfg = event_config_new();
/* Run at most 16 callbacks beforechecking for other events. */
event_config_set_max_dispatch_interval(cfg, NULL, 16, 0);
struct event_base *base =event_base_new_with_config(cfg);
ev = event_new(base, -1, EV_PERSIST| EV_READ, cb, NULL);
event_add(ev, NULL);
event_active(ev, EV_WRITE, 0);
event_base_loop(base, 0);
return 0;
}
該方法在 <event2/event.h>中定義。
十三:優化通常性超時
當前版本的Libevent使用二叉堆算法來對」掛起」狀態的event超時時間值進行跟蹤。對於有序的添加和刪除event超時時間的操做,二叉堆算法能夠提供O(lg n)的性能。這對於添加隨機分佈的超時時間來講,性能是最優的,可是若是是大量相同時間的events來講就不是了。
好比,假設有一萬個事件,每個event的超時時間都是在他們被添加以後的5秒鐘。在這種狀況下,使用雙向隊列實現的話,能夠達到O(1)的性能。
正常狀況下,通常不但願使用隊列管理全部的超時時間值,由於隊列僅對於恆定的超時時間來講是快速的。若是一些超時時間或多或少的隨機分佈的話,那添加這些超時時間到隊列將會花費O(n)的時間,這樣的性能要比二叉堆差多了。
Libevent解決這種問題的方法是將一些超時時間值放置在隊列中,其餘的則放入二叉堆中。能夠向Libevent請求一個「公用超時時間」的時間值,而後使用該時間值進行事件的添加。若是存在大量的event,它們的超時時間都是這種單一公用超時時間的狀況,那麼使用這種優化的方法能夠明顯提升超時事件的性能。
const struct timeval * event_base_init_common_timeout(
struct event_base *base, const struct timeval* duration);
該方法的參數有event_base,以及一個用來初始化的公用超時時間值。該函數返回一個指向特殊timeval結構體的指針,可使用該指針代表將event添加到O(1)的隊列中,而不是O(lg n)的堆中。這個特殊的timeval結構能夠在代碼中自由的複製和分配。該timeval只能工做在特定的event_base上(參數)。不要依賴於該timeval的實際值:Libevent僅使用它們來指明使用哪一個隊列。
#include <event2/event.h>
#include <string.h>
/*We're going to create a verylarge number of events on a given base,
* nearly all of which have a ten-secondtimeout. If initialize_timeout
* is called, we'll tell Libevent to add theten-second ones to an O(1)
* queue. */
struct timeval ten_seconds = { 10, 0 };
void initialize_timeout(struct event_base *base)
{
struct timeval tv_in = { 10, 0 };
const struct timeval*tv_out;
tv_out =event_base_init_common_timeout(base, &tv_in);
memcpy(&ten_seconds,tv_out, sizeof(struct timeval));
}
int my_event_add(struct event *ev, const struct timeval*tv)
{
/* Note that ev must have the sameevent_base that we passed to
initialize_timeout */
if (tv && tv->tv_sec == 10 && tv->tv_usec == 0)
return event_add(ev, &ten_seconds);
else
return event_add(ev, tv);
}
相似於其餘全部的優化函數,除非肯定對你有用,不然應該避免使用這種公用超時時間功能。
十四:從已清除的內存識別事件
Libevent提供了這樣的函數,能夠從已經清0的內存中(好比以calloc分配,或者經過memset或bzero清除)識別出已初始化的event。
int event_initialized(const struct event*ev);
#define evsignal_initialized(ev) event_initialized(ev)
#define evtimer_initialized(ev) event_initialized(ev)
警告:這些函數不能在一塊未初始化的內存中識別出已經初始化了的event。除非你能肯定該內存要麼被清0,要麼被初始化爲event,不然不要使用這些函數。
通常狀況下,除非你的應用程序有着極爲特殊的需求,不然不要輕易使用這些函數。經過event_new返回的events永遠已經初始化過的。
#include <event2/event.h>
#include <stdlib.h>
struct reader {
evutil_socket_t fd;
};
#defineREADER_ACTUAL_SIZE() (sizeof(struct reader) + event_get_struct_event_size())
#defineREADER_EVENT_PTR(r) ((struct event *) (((char*)(r))+sizeof(struct reader)))
struct reader * allocate_reader(evutil_socket_t fd)
{
struct reader *r = calloc(1, READER_ACTUAL_SIZE());
if (r)
r->fd = fd;
return r;
}
void readcb(evutil_socket_t, short, void*);
int add_reader(struct reader *r, struct event_base *b)
{
struct event *ev= READER_EVENT_PTR(r);
if (!event_initialized(ev))
event_assign(ev, b, r->fd, EV_READ, readcb, r);
return event_add(ev, NULL);
}
十五:過期的event處理函數
在Libevent2.0以前的版本中,沒有event_assign或者event_new函數,而只有event_set函數,該函數返回的event與「當前」base相關聯。若是有多個event_base,則還須要調用event_base_set函數指明event與哪一個base相關聯。
void event_set(struct event *event, evutil_socket_t fd, short what,
void(*callback)(evutil_socket_t, short, void *), void *arg);
int event_base_set(struct event_base * base, struct event *event);
event_set函數相似於event_assign,除了它使用「當前」base的概念。event_base_set函數改變event所關聯的base。
若是是處理超時或者信號events,event_set也有一些便於使用的變種:evtimer_set相似於evtimer_assign,而evsignal_set相似於evsignal_assign。
Libevent2.0以前的版本中,使用以signal_爲前綴的函數做爲處理基於信號的event_set的變種。而不是evsignal_(也就是說是:signal_set, signal_add, signal_del, signal_pending和signal_intialized)。Libevent古老版本(0.6以前),使用timeout_,而不是evtimer_前綴。所以,若是你須要處理很老的代碼的話,可能會看見timeout_add(), timeout_del(), timeout_initialized(), timeout_set(), timeout_pending()等。
較老版本的Libevent(2.0以前)使用兩個宏,完成event_get_fd和event_get_signal的工做:EVENT_FD和EVENT_SIGNAL。這些宏直接監測event結構的內部,所以在各類版本之間不具備二進制兼容性。在2.0以及以後的版本中,這些宏就是event_get_fd和event_get_signal函數的別名。
在Libevent2.0以前的版本中不支持鎖操做,所以,在運行base的線程以外的線程中,調用任何改變event狀態的函數,都是不安全的。這些函數包括:event_add, event_del, event_active, and event_base_once。
event_base_once的古老版本是event_once,它使用「當前」base的概念。
在Libevent2.0以前,超時事件設置EV_PERSISIT是不明智的。並不是在event激活時重置超時時間,EV_PERSISIT標誌對於超時而言無任何做用。
Libevent2.0以前的版本不支持多個events,在同一時間添加一樣的fd以及相同的READ/WRITE事件。換句話說,對於每個fd,一次只能有一個event在其上等待讀或寫。
http://www.wangafu.net/~nickm/libevent-book/Ref4_event.html