http://blog.csdn.net/tricky1997/article/details/7385021
libevent的源代码间间断断的看了有一些时间了,重点一直在实现的细节,每段代码都能看得差不多但就是不知道整个流程是怎样的。
昨天偶然发现了张亮的源代码分析,花了十几分钟过了一遍,豁然开朗!如果对libevent有个整体认识,很容易看懂。下午重看了几个主要函数,一下就串起来了,随手做了做笔记。libevent虽然没那么精炼,相比于libev的实现,还是非常易读的。
这篇与张亮那篇不同,相当于对libevent的主要的几个函数部分做一个伪代码翻译,以后工作中如果需要细读的话,能尽快再拾起来。
只涉及到主要部分,也就是对事件I/O以及定时的实现。像其他的信号处理,bufferevent,http,dns等非重点部分不再仔细研究。libevent在这几个方面的实现不如它事件I/O的实现应用的那么广泛。
代码版本2.0.16。源代码中关于多线程支持,信号处理和windows部分可能会删掉。
event_base相关
event_base_new
{
获取默认cfg;
调用event_base_new_with_config
释放掉cfh;
}
- struct event_base *
- event_base_new(void)
- {
- struct event_base *base = NULL;
- struct event_config *cfg = event_config_new();
- if (cfg) {
- base = event_base_new_with_config(cfg);
- event_config_free(cfg);
- }
- return base;
- }
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
event_base_new_with_config
{
mm_calloc分配内存;
检测是否支持CLOCK_MONOTONIC(因为定时器最好可以保持时间的单调性,之后可以看到如果系统支持clock_gettime(CLOCK_MONOTONIC,tp)将会比较方便);
gettime(见后文);
初始化time_heap;
初始化eventqueue;
初始化信号使用的pair,初始化多线程时使用的pair;
初始化deferred_queue;
将config的flag复制到event_base;
初始化event_io_map;(event_io_map有两种实现方式:基于hash表的;类似于event_signal_map的。当使用第二种时,event_io_map及相应的操作相当于event_sigal_map及其操作的别名)
初始化event_signal_map;
遍历全局变量eventops,找到适合的backend,调用其init函数(init函数里会调用处理信号的evsig_init(base)),保存backend使用的特定数据;
初始化优先级队列event_base_priority_init(见下文);
}
- struct event_base *
- event_base_new_with_config(const struct event_config *cfg)
- {
- int i;
- struct event_base *base;
- int should_check_environment;
- if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
- event_warn("%s: calloc", __func__);
- return NULL;
- }
- detect_monotonic();
- gettime(base, &base->event_tv);
- min_heap_ctor(&base->timeheap);
- TAILQ_INIT(&base->eventqueue);
- base->sig.ev_signal_pair[0] = -1;
- base->sig.ev_signal_pair[1] = -1;
- base->th_notify_fd[0] = -1;
- base->th_notify_fd[1] = -1;
- event_deferred_cb_queue_init(&base->defer_queue);
- base->defer_queue.notify_fn = notify_base_cbq_callback;
- base->defer_queue.notify_arg = base;
- if (cfg)
- base->flags = cfg->flags;
- evmap_io_initmap(&base->io);
- evmap_signal_initmap(&base->sigmap);
- event_changelist_init(&base->changelist);
- base->evbase = NULL;
- should_check_environment =
- !(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
- for (i = 0; eventops[i] && !base->evbase; i++) {
- if (cfg != NULL) {
- /* determine if this backend should be avoided */
- if (event_config_is_avoided_method(cfg,
- eventops[i]->name))
- continue;
- if ((eventops[i]->features & cfg->require_features)
- != cfg->require_features)
- continue;
- }
- /* also obey the environment variables */
- if (should_check_environment &&
- event_is_method_disabled(eventops[i]->name))
- continue;
- base->evsel = eventops[i];
- base->evbase = base->evsel->init(base);
- }
- if (base->evbase == NULL) {
- event_warnx("%s: no event mechanism available",
- __func__);
- base->evsel = NULL;
- event_base_free(base);
- return NULL;
- }
- if (evutil_getenv("EVENT_SHOW_METHOD"))
- event_msgx("libevent using: %s", base->evsel->name);
- /* allocate a single active event queue */
- if (event_base_priority_init(base, 1) < 0) {
- event_base_free(base);
- return NULL;
- }
- return (base);
- }
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
int i;
struct event_base *base;
int should_check_environment;
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
event_warn("%s: calloc", __func__);
return NULL;
}
detect_monotonic();
gettime(base, &base->event_tv);
min_heap_ctor(&base->timeheap);
TAILQ_INIT(&base->eventqueue);
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
event_deferred_cb_queue_init(&base->defer_queue);
base->defer_queue.notify_fn = notify_base_cbq_callback;
base->defer_queue.notify_arg = base;
if (cfg)
base->flags = cfg->flags;
evmap_io_initmap(&base->io);
evmap_signal_initmap(&base->sigmap);
event_changelist_init(&base->changelist);
base->evbase = NULL;
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
if (event_config_is_avoided_method(cfg,
eventops[i]->name))
continue;
if ((eventops[i]->features & cfg->require_features)
!= cfg->require_features)
continue;
}
/* also obey the environment variables */
if (should_check_environment &&
event_is_method_disabled(eventops[i]->name))
continue;
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
if (base->evbase == NULL) {
event_warnx("%s: no event mechanism available",
__func__);
base->evsel = NULL;
event_base_free(base);
return NULL;
}
if (evutil_getenv("EVENT_SHOW_METHOD"))
event_msgx("libevent using: %s", base->evsel->name);
/* allocate a single active event queue */
if (event_base_priority_init(base, 1) < 0) {
event_base_free(base);
return NULL;
}
return (base);
}
gettime
{
尝试从event_base的cache中获得时间;
如果use_monotonic==1,则通过clock_gettime(CLOCK_MONOTONIC, &ts)获得时间。并对base->tv_clock_diff和base->last_updated_clock_diff修正(它会在event_base_gettimeofday_cached中使用,这不重要,不细说了);
否则,evutil_gettimeofday(evutil_gettimeofday在非windows平台上一般直接对应系统的gettimeofday,在windows平台用_ftime实现);
}
- static int
- gettime(struct event_base *base, struct timeval *tp)
- {
- EVENT_BASE_ASSERT_LOCKED(base);
- if (base->tv_cache.tv_sec) {
- *tp = base->tv_cache;
- return (0);
- }
- #if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- if (use_monotonic) {
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
- return (-1);
- tp->tv_sec = ts.tv_sec;
- tp->tv_usec = ts.tv_nsec / 1000;
- if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL
- < ts.tv_sec) {
- struct timeval tv;
- evutil_gettimeofday(&tv,NULL);
- evutil_timersub(&tv, tp, &base->tv_clock_diff);
- base->last_updated_clock_diff = ts.tv_sec;
- }
- return (0);
- }
- #endif
- return (evutil_gettimeofday(tp, NULL));
- }
static int
gettime(struct event_base *base, struct timeval *tp)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (base->tv_cache.tv_sec) {
*tp = base->tv_cache;
return (0);
}
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
if (use_monotonic) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
return (-1);
tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000;
if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL
< ts.tv_sec) {
struct timeval tv;
evutil_gettimeofday(&tv,NULL);
evutil_timersub(&tv, tp, &base->tv_clock_diff);
base->last_updated_clock_diff = ts.tv_sec;
}
return (0);
}
#endif
return (evutil_gettimeofday(tp, NULL));
}
event_base_priority_init
{
如果已经有active event,直接返回;
初始化event_base中npriorityies个队列指针以及数量;
}
- int
- event_base_priority_init(struct event_base *base, int npriorities)
- {
- int i;
- if (N_ACTIVE_CALLBACKS(base) || npriorities < 1
- || npriorities >= EVENT_MAX_PRIORITIES)
- return (-1);
- if (npriorities == base->nactivequeues)
- return (0);
- if (base->nactivequeues) {
- mm_free(base->activequeues);
- base->nactivequeues = 0;
- }
- /* Allocate our priority queues */
- base->activequeues = (struct event_list *)
- mm_calloc(npriorities, sizeof(struct event_list));
- if (base->activequeues == NULL) {
- event_warn("%s: calloc", __func__);
- return (-1);
- }
- base->nactivequeues = npriorities;
- for (i = 0; i < base->nactivequeues; ++i) {
- TAILQ_INIT(&base->activequeues[i]);
- }
- return (0);
- }
int
event_base_priority_init(struct event_base *base, int npriorities)
{
int i;
if (N_ACTIVE_CALLBACKS(base) || npriorities < 1
|| npriorities >= EVENT_MAX_PRIORITIES)
return (-1);
if (npriorities == base->nactivequeues)
return (0);
if (base->nactivequeues) {
mm_free(base->activequeues);
base->nactivequeues = 0;
}
/* Allocate our priority queues */
base->activequeues = (struct event_list *)
mm_calloc(npriorities, sizeof(struct event_list));
if (base->activequeues == NULL) {
event_warn("%s: calloc", __func__);
return (-1);
}
base->nactivequeues = npriorities;
for (i = 0; i < base->nactivequeues; ++i) {
TAILQ_INIT(&base->activequeues[i]);
}
return (0);
}
在继续之前,插一下event_base中几个队列的性质。
EVLIST_INSERTED 一般的I/O event的队列 插入到eventqueue
EVLIST_TIMEOUT 定时的event 的队列 插入到time_heap或者common_timeout_list
EVLIST_ACTIVE active event的队列 插入到activequeues
一个event可以被插入到不同队列中,例如带计时器的I/O event可以同时被放进eventqueue和time_heap。
event_count是这三种队列里event数量的总和。
基本上每个队列也都有各自的计数变量。
event相关
event_new
{
申请一块内存;
event_assign;
}
event_assign
{
参数赋值到event上;
ev->flags置为EVLIST_INIT;(ev->flags用来保存EVLIST_*这种标志,ev->res用来保存EV_*这种标志,后边会遇到)
switch(event的类型)
EV_SIGNAL: ev->ev_closure = EV_CLOSURE_SIGNAL;
EV_PERSIST: ev->ev_closure = EV_CLOSURE_PERSIST;
其他: ev->ev_closure = EV_CLOSURE_NONE;
event优先级初始化为1/2 * base的优先级队列个数;
}
- int
- event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
- {
- if (!base)
- base = current_base;
- _event_debug_assert_not_added(ev);
- ev->ev_base = base;
- ev->ev_callback = callback;
- ev->ev_arg = arg;
- ev->ev_fd = fd;
- ev->ev_events = events;
- ev->ev_res = 0;
- ev->ev_flags = EVLIST_INIT;
- ev->ev_ncalls = 0;
- ev->ev_pncalls = NULL;
- if (events & EV_SIGNAL) {
- if ((events & (EV_READ|EV_WRITE)) != 0) {
- event_warnx("%s: EV_SIGNAL is not compatible with "
- "EV_READ or EV_WRITE", __func__);
- return -1;
- }
- ev->ev_closure = EV_CLOSURE_SIGNAL;
- } else {
- if (events & EV_PERSIST) {
- evutil_timerclear(&ev->ev_io_timeout);
- ev->ev_closure = EV_CLOSURE_PERSIST;
- } else {
- ev->ev_closure = EV_CLOSURE_NONE;
- }
- }
- min_heap_elem_init(ev);
- if (base != NULL) {
- /* by default, we put new events into the middle priority */
- ev->ev_pri = base->nactivequeues / 2;
- }
- _event_debug_note_setup(ev);
- return 0;
- }
int
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
if (!base)
base = current_base;
_event_debug_assert_not_added(ev);
ev->ev_base = base;
ev->ev_callback = callback;
ev->ev_arg = arg;
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_res = 0;
ev->ev_flags = EVLIST_INIT;
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
if (events & EV_SIGNAL) {
if ((events & (EV_READ|EV_WRITE)) != 0) {
event_warnx("%s: EV_SIGNAL is not compatible with "
"EV_READ or EV_WRITE", __func__);
return -1;
}
ev->ev_closure = EV_CLOSURE_SIGNAL;
} else {
if (events & EV_PERSIST) {
evutil_timerclear(&ev->ev_io_timeout);
ev->ev_closure = EV_CLOSURE_PERSIST;
} else {
ev->ev_closure = EV_CLOSURE_NONE;
}
}
min_heap_elem_init(ev);
if (base != NULL) {
/* by default, we put new events into the middle priority */
ev->ev_pri = base->nactivequeues / 2;
}
_event_debug_note_setup(ev);
return 0;
}
event_add_internal
{
如果有定时的话,在time_heap上预留一个位置;(time_heap使用的内存的分配采用了指数次扩展的方式,初始化一个值,每次不够用时^2一下。类似的策略在libevent里的多个队列中都可以看到使用)
如果是I/O或者信号而且不在active队列 {
对于I/O事件:evmap_io_add(见下文)
对于信号时间:evmap_signal_add
event_queue_insert(... EVLIST_INSERTED)(见下文)
}
如果插入正常而且有定时{
如果ev->ev_closure==EV_CLOSURE_PERSIST且使用了相对时间(比如,当我们正常调用event_add()时,我们可能将时间设为10s,这就是相对时间),就保存时间到union的另一个变量里;
如果已经在time_heap里或者active了,就先把它从相应队列里删掉;
event_queue_insert(... EVLIST_TIMEOUT);(见下文)
如果是个插入common_time_list的事件,调度一下。
}
}
- static inline int
- event_add_internal(struct event *ev, const struct timeval *tv,
- int tv_is_absolute)
- {
- struct event_base *base = ev->ev_base;
- int res = 0;
- int notify = 0;
- EVENT_BASE_ASSERT_LOCKED(base);
- _event_debug_assert_is_setup(ev);
- event_debug((
- "event_add: event: %p (fd %d), %s%s%scall %p",
- ev,
- (int)ev->ev_fd,
- ev->ev_events & EV_READ ? "EV_READ " : " ",
- ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
- tv ? "EV_TIMEOUT " : " ",
- ev->ev_callback));
- EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
- /*
- * prepare for timeout insertion further below, if we get a
- * failure on any step, we should not change any state.
- */
- if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
- if (min_heap_reserve(&base->timeheap,
- 1 + min_heap_size(&base->timeheap)) == -1)
- return (-1); /* ENOMEM == errno */
- }
- if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
- !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
- if (ev->ev_events & (EV_READ|EV_WRITE))
- res = evmap_io_add(base, ev->ev_fd, ev);
- else if (ev->ev_events & EV_SIGNAL)
- res = evmap_signal_add(base, (int)ev->ev_fd, ev);
- if (res != -1)
- event_queue_insert(base, ev, EVLIST_INSERTED);
- if (res == 1) {
- /* evmap says we need to notify the main thread. */
- notify = 1;
- res = 0;
- }
- }
- /*
- * we should change the timeout state only if the previous event
- * addition succeeded.
- */
- if (res != -1 && tv != NULL) {
- struct timeval now;
- int common_timeout;
- /*
- * for persistent timeout events, we remember the
- * timeout value and re-add the event.
- *
- * If tv_is_absolute, this was already set.
- */
- if (ev->ev_closure == EV_CLOSURE_PERSIST && !tv_is_absolute)
- ev->ev_io_timeout = *tv;
- /*
- * we already reserved memory above for the case where we
- * are not replacing an existing timeout.
- */
- if (ev->ev_flags & EVLIST_TIMEOUT) {
- /* XXX I believe this is needless. */
- if (min_heap_elt_is_top(ev))
- notify = 1;
- event_queue_remove(base, ev, EVLIST_TIMEOUT);
- }
- /* Check if it is active due to a timeout. Rescheduling
- * this timeout before the callback can be executed
- * removes it from the active list. */
- if ((ev->ev_flags & EVLIST_ACTIVE) &&
- (ev->ev_res & EV_TIMEOUT)) {
- if (ev->ev_events & EV_SIGNAL) {
- /* See if we are just active executing
- * this event in a loop
- */
- if (ev->ev_ncalls && ev->ev_pncalls) {
- /* Abort loop */
- *ev->ev_pncalls = 0;
- }
- }
- event_queue_remove(base, ev, EVLIST_ACTIVE);
- }
- gettime(base, &now);
- common_timeout = is_common_timeout(tv, base);
- if (tv_is_absolute) {
- ev->ev_timeout = *tv;
- } else if (common_timeout) {
- struct timeval tmp = *tv;
- tmp.tv_usec &= MICROSECONDS_MASK;
- evutil_timeradd(&now, &tmp, &ev->ev_timeout);
- ev->ev_timeout.tv_usec |=
- (tv->tv_usec & ~MICROSECONDS_MASK);
- } else {
- evutil_timeradd(&now, tv, &ev->ev_timeout);
- }
- event_debug((
- "event_add: timeout in %d seconds, call %p",
- (int)tv->tv_sec, ev->ev_callback));
- event_queue_insert(base, ev, EVLIST_TIMEOUT);
- if (common_timeout) {
- struct common_timeout_list *ctl =
- get_common_timeout_list(base, &ev->ev_timeout);
- if (ev == TAILQ_FIRST(&ctl->events)) {
- common_timeout_schedule(ctl, &now, ev);
- }
- } else {
- /* See if the earliest timeout is now earlier than it
- * was before: if so, we will need to tell the main
- * thread to wake up earlier than it would
- * otherwise. */
- if (min_heap_elt_is_top(ev))
- notify = 1;
- }
- }
- /* if we are not in the right thread, we need to wake up the loop */
- if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
- evthread_notify_base(base);
- _event_debug_note_add(ev);
- return (res);
- }
static inline int
event_add_internal(struct event *ev, const struct timeval *tv,
int tv_is_absolute)
{
struct event_base *base = ev->ev_base;
int res = 0;
int notify = 0;
EVENT_BASE_ASSERT_LOCKED(base);
_event_debug_assert_is_setup(ev);
event_debug((
"event_add: event: %p (fd %d), %s%s%scall %p",
ev,
(int)ev->ev_fd,
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback));
EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
/*
* prepare for timeout insertion further below, if we get a
* failure on any step, we should not change any state.
*/
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
if (min_heap_reserve(&base->timeheap,
1 + min_heap_size(&base->timeheap)) == -1)
return (-1); /* ENOMEM == errno */
}
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
if (ev->ev_events & (EV_READ|EV_WRITE))
res = evmap_io_add(base, ev->ev_fd, ev);
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add(base, (int)ev->ev_fd, ev);
if (res != -1)
event_queue_insert(base, ev, EVLIST_INSERTED);
if (res == 1) {
/* evmap says we need to notify the main thread. */
notify = 1;
res = 0;
}
}
/*
* we should change the timeout state only if the previous event
* addition succeeded.
*/
if (res != -1 && tv != NULL) {
struct timeval now;
int common_timeout;
/*
* for persistent timeout events, we remember the
* timeout value and re-add the event.
*
* If tv_is_absolute, this was already set.
*/
if (ev->ev_closure == EV_CLOSURE_PERSIST && !tv_is_absolute)
ev->ev_io_timeout = *tv;
/*
* we already reserved memory above for the case where we
* are not replacing an existing timeout.
*/
if (ev->ev_flags & EVLIST_TIMEOUT) {
/* XXX I believe this is needless. */
if (min_heap_elt_is_top(ev))
notify = 1;
event_queue_remove(base, ev, EVLIST_TIMEOUT);
}
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
if (ev->ev_events & EV_SIGNAL) {
/* See if we are just active executing
* this event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
}
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
gettime(base, &now);
common_timeout = is_common_timeout(tv, base);
if (tv_is_absolute) {
ev->ev_timeout = *tv;
} else if (common_timeout) {
struct timeval tmp = *tv;
tmp.tv_usec &= MICROSECONDS_MASK;
evutil_timeradd(&now, &tmp, &ev->ev_timeout);
ev->ev_timeout.tv_usec |=
(tv->tv_usec & ~MICROSECONDS_MASK);
} else {
evutil_timeradd(&now, tv, &ev->ev_timeout);
}
event_debug((
"event_add: timeout in %d seconds, call %p",
(int)tv->tv_sec, ev->ev_callback));
event_queue_insert(base, ev, EVLIST_TIMEOUT);
if (common_timeout) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
if (ev == TAILQ_FIRST(&ctl->events)) {
common_timeout_schedule(ctl, &now, ev);
}
} else {
/* See if the earliest timeout is now earlier than it
* was before: if so, we will need to tell the main
* thread to wake up earlier than it would
* otherwise. */
if (min_heap_elt_is_top(ev))
notify = 1;
}
}
/* if we are not in the right thread, we need to wake up the loop */
if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base);
_event_debug_note_add(ev);
return (res);
}
evmap_io_add
{
如果使用hash,先保证内存足够;
调用GET_IO_SLOT_AND_CTOR;(在是否使用hash时,会有不同的实现)
old指示之前是否有其他的event读写该fd;(用于changelist优化)
如果是第一个读或者写,res或上EV_READ/EV_WRITE;
res!=0时,表示之前这个fd没有读或写,调用evsel->add;
更新fd对应的slot里的nreads/nwrites;
将event插入fd对应的slot的events队列。
}
- int
- evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
- {
- const struct eventop *evsel = base->evsel;
- struct event_io_map *io = &base->io;
- struct evmap_io *ctx = NULL;
- int nread, nwrite, retval = 0;
- short res = 0, old = 0;
- struct event *old_ev;
- EVUTIL_ASSERT(fd == ev->ev_fd);
- if (fd < 0)
- return 0;
- #ifndef EVMAP_USE_HT
- if (fd >= io->nentries) {
- if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
- return (-1);
- }
- #endif
- GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
- evsel->fdinfo_len);
- nread = ctx->nread;
- nwrite = ctx->nwrite;
- if (nread)
- old |= EV_READ;
- if (nwrite)
- old |= EV_WRITE;
- if (ev->ev_events & EV_READ) {
- if (++nread == 1)
- res |= EV_READ;
- }
- if (ev->ev_events & EV_WRITE) {
- if (++nwrite == 1)
- res |= EV_WRITE;
- }
- if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff)) {
- event_warnx("Too many events reading or writing on fd %d",
- (int)fd);
- return -1;
- }
- if (EVENT_DEBUG_MODE_IS_ON() &&
- (old_ev = TAILQ_FIRST(&ctx->events)) &&
- (old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
- event_warnx("Tried to mix edge-triggered and non-edge-triggered"
- " events on fd %d", (int)fd);
- return -1;
- }
- if (res) {
- void *extra = ((char*)ctx) + sizeof(struct evmap_io);
- /* XXX(niels): we cannot mix edge-triggered and
- * level-triggered, we should probably assert on
- * this. */
- if (evsel->add(base, ev->ev_fd,
- old, (ev->ev_events & EV_ET) | res, extra) == -1)
- return (-1);
- retval = 1;
- }
- ctx->nread = (ev_uint16_t) nread;
- ctx->nwrite = (ev_uint16_t) nwrite;
- TAILQ_INSERT_TAIL(&ctx->events, ev, ev_io_next);
- return (retval);
- }
int
evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
{
const struct eventop *evsel = base->evsel;
struct event_io_map *io = &base->io;
struct evmap_io *ctx = NULL;
int nread, nwrite, retval = 0;
short res = 0, old = 0;
struct event *old_ev;
EVUTIL_ASSERT(fd == ev->ev_fd);
if (fd < 0)
return 0;
#ifndef EVMAP_USE_HT
if (fd >= io->nentries) {
if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
return (-1);
}
#endif
GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
evsel->fdinfo_len);
nread = ctx->nread;
nwrite = ctx->nwrite;
if (nread)
old |= EV_READ;
if (nwrite)
old |= EV_WRITE;
if (ev->ev_events & EV_READ) {
if (++nread == 1)
res |= EV_READ;
}
if (ev->ev_events & EV_WRITE) {
if (++nwrite == 1)
res |= EV_WRITE;
}
if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff)) {
event_warnx("Too many events reading or writing on fd %d",
(int)fd);
return -1;
}
if (EVENT_DEBUG_MODE_IS_ON() &&
(old_ev = TAILQ_FIRST(&ctx->events)) &&
(old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
event_warnx("Tried to mix edge-triggered and non-edge-triggered"
" events on fd %d", (int)fd);
return -1;
}
if (res) {
void *extra = ((char*)ctx) + sizeof(struct evmap_io);
/* XXX(niels): we cannot mix edge-triggered and
* level-triggered, we should probably assert on
* this. */
if (evsel->add(base, ev->ev_fd,
old, (ev->ev_events & EV_ET) | res, extra) == -1)
return (-1);
retval = 1;
}
ctx->nread = (ev_uint16_t) nread;
ctx->nwrite = (ev_uint16_t) nwrite;
TAILQ_INSERT_TAIL(&ctx->events, ev, ev_io_next);
return (retval);
}
event_queue_insert
{
如果已经加入了某队列而且不是active队列,则返回;(允许插入active队列两次)
base->evnet_count++;
switch(类型):
EVLIST_INSERTED: 插入到eventqueue
EVLIST_ACTIVE: 插入到activequeues
EVLIST_TIMEOUT: 插入到time_heap或者common_timeout_queues
}
- static void
- event_queue_insert(struct event_base *base, struct event *ev, int queue)
- {
- EVENT_BASE_ASSERT_LOCKED(base);
- if (ev->ev_flags & queue) {
- /* Double insertion is possible for active events */
- if (queue & EVLIST_ACTIVE)
- return;
- event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
- ev, ev->ev_fd, queue);
- return;
- }
- if (~ev->ev_flags & EVLIST_INTERNAL)
- base->event_count++;
- ev->ev_flags |= queue;
- switch (queue) {
- case EVLIST_INSERTED:
- TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
- break;
- case EVLIST_ACTIVE:
- base->event_count_active++;
- TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],
- ev,ev_active_next);
- break;
- case EVLIST_TIMEOUT: {
- if (is_common_timeout(&ev->ev_timeout, base)) {
- struct common_timeout_list *ctl =
- get_common_timeout_list(base, &ev->ev_timeout);
- insert_common_timeout_inorder(ctl, ev);
- } else
- min_heap_push(&base->timeheap, ev);
- break;
- }
- default:
- event_errx(1, "%s: unknown queue %x", __func__, queue);
- }
- }
static void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (ev->ev_flags & queue) {
/* Double insertion is possible for active events */
if (queue & EVLIST_ACTIVE)
return;
event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
ev, ev->ev_fd, queue);
return;
}
if (~ev->ev_flags & EVLIST_INTERNAL)
base->event_count++;
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_INSERTED:
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
break;
case EVLIST_ACTIVE:
base->event_count_active++;
TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],
ev,ev_active_next);
break;
case EVLIST_TIMEOUT: {
if (is_common_timeout(&ev->ev_timeout, base)) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
insert_common_timeout_inorder(ctl, ev);
} else
min_heap_push(&base->timeheap, ev);
break;
}
default:
event_errx(1, "%s: unknown queue %x", __func__, queue);
}
}
event_queue_remove类似于event_queue_insert:
--base->event_count;
对应不同的类型,从不同的队列中删除
event_base_loop相关
event_base_loop
{
置running_loop = 1;
清空时间缓存;
设置信号处理相关的全局变量;
event_gotterm和event_break清0;
while(!done){
检测并相应event_gotterm和event_break;
修正时间timecorrect;(如果时间回滚,将time_heap的所有时间减去差)
如果有活动event或者指定标识EVLOOP_NONBLOCK,清空时间tv。否则计算一下要等待下一个定时事件发生的时间。
如果没有event,直接返回;
更新上次的base->event_tv;
清空时间缓存;
调用后端的dispatch函数;
更新时间缓存;
处理time_heap中已过期的event: timeout_process;(从time_heap中取出,放入activequeues,见下文)
如果有active event{
处理active event: event_process_active;(见下文)
如果标识有EVLOOP_ONCE,而且没有了active event,而且刚才处理了几个,那么可以done=1;
}否则,若指定NONBLOCK,也done-1;
}
}
- int
- event_base_loop(struct event_base *base, int flags)
- {
- const struct eventop *evsel = base->evsel;
- struct timeval tv;
- struct timeval *tv_p;
- int res, done, retval = 0;
- /* Grab the lock. We will release it inside evsel.dispatch, and again
- * as we invoke user callbacks. */
- EVBASE_ACQUIRE_LOCK(base, th_base_lock);
- if (base->running_loop) {
- event_warnx("%s: reentrant invocation. Only one event_base_loop"
- " can run on each event_base at once.", __func__);
- EVBASE_RELEASE_LOCK(base, th_base_lock);
- return -1;
- }
- base->running_loop = 1;
- clear_time_cache(base);
- if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
- evsig_set_base(base);
- done = 0;
- base->event_gotterm = base->event_break = 0;
- while (!done) {
- /* Terminate the loop if we have been asked to */
- if (base->event_gotterm) {
- break;
- }
- if (base->event_break) {
- break;
- }
- timeout_correct(base, &tv);
- tv_p = &tv;
- if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
- timeout_next(base, &tv_p);
- } else {
- /*
- * if we have active events, we just poll new events
- * without waiting.
- */
- evutil_timerclear(&tv);
- }
- /* If we have no events, we just exit */
- if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
- event_debug(("%s: no events registered.", __func__));
- retval = 1;
- goto done;
- }
- /* update last old time */
- gettime(base, &base->event_tv);
- clear_time_cache(base);
- res = evsel->dispatch(base, tv_p);
- if (res == -1) {
- event_debug(("%s: dispatch returned unsuccessfully.",
- __func__));
- retval = -1;
- goto done;
- }
- 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;
- }
- event_debug(("%s: asked to terminate loop.", __func__));
- done:
- clear_time_cache(base);
- base->running_loop = 0;
- EVBASE_RELEASE_LOCK(base, th_base_lock);
- return (retval);
- }
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
struct timeval tv;
struct timeval *tv_p;
int res, done, retval = 0;
/* Grab the lock. We will release it inside evsel.dispatch, and again
* as we invoke user callbacks. */
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (base->running_loop) {
event_warnx("%s: reentrant invocation. Only one event_base_loop"
" can run on each event_base at once.", __func__);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return -1;
}
base->running_loop = 1;
clear_time_cache(base);
if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
evsig_set_base(base);
done = 0;
base->event_gotterm = base->event_break = 0;
while (!done) {
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
break;
}
if (base->event_break) {
break;
}
timeout_correct(base, &tv);
tv_p = &tv;
if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
evutil_timerclear(&tv);
}
/* If we have no events, we just exit */
if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = 1;
goto done;
}
/* update last old time */
gettime(base, &base->event_tv);
clear_time_cache(base);
res = evsel->dispatch(base, tv_p);
if (res == -1) {
event_debug(("%s: dispatch returned unsuccessfully.",
__func__));
retval = -1;
goto done;
}
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;
}
event_debug(("%s: asked to terminate loop.", __func__));
done:
clear_time_cache(base);
base->running_loop = 0;
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (retval);
}
timeout_process
{
while(栈顶event过期){
从所有可能的队列中删掉event;
active_nonblock( ... EV_TIMEOUT);(插入到active队列,并在ev_res中设置EV_TIMEOUT标识)
}
}
- static void
- timeout_process(struct event_base *base)
- {
- /* Caller must hold lock. */
- struct timeval now;
- struct event *ev;
- if (min_heap_empty(&base->timeheap)) {
- return;
- }
- gettime(base, &now);
- while ((ev = min_heap_top(&base->timeheap))) {
- if (evutil_timercmp(&ev->ev_timeout, &now, >))
- break;
- /* delete this event from the I/O queues */
- event_del_internal(ev);
- event_debug(("timeout_process: call %p",
- ev->ev_callback));
- event_active_nolock(ev, EV_TIMEOUT, 1);
- }
- }
static void
timeout_process(struct event_base *base)
{
/* Caller must hold lock. */
struct timeval now;
struct event *ev;
if (min_heap_empty(&base->timeheap)) {
return;
}
gettime(base, &now);
while ((ev = min_heap_top(&base->timeheap))) {
if (evutil_timercmp(&ev->ev_timeout, &now, >))
break;
/* delete this event from the I/O queues */
event_del_internal(ev);
event_debug(("timeout_process: call %p",
ev->ev_callback));
event_active_nolock(ev, EV_TIMEOUT, 1);
}
}
event_process_active
{
从低优先级向高优先级队列依次调用event_process_active_single_queue;(见下文)
处理deferred callbacks;(deferredqueue用于bufferevent中,不再细说)
}
- static int
- event_process_active(struct event_base *base)
- {
- /* Caller must hold th_base_lock */
- struct event_list *activeq = NULL;
- int i, c = 0;
- for (i = 0; i < base->nactivequeues; ++i) {
- if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {
- activeq = &base->activequeues[i];
- c = event_process_active_single_queue(base, activeq);
- if (c < 0)
- return -1;
- else if (c > 0)
- break; /* Processed a real event; do not
- * consider lower-priority events */
- /* If we get here, all of the events we processed
- * were internal. Continue. */
- }
- }
- event_process_deferred_callbacks(&base->defer_queue,&base->event_break);
- return c;
- }
static int
event_process_active(struct event_base *base)
{
/* Caller must hold th_base_lock */
struct event_list *activeq = NULL;
int i, c = 0;
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {
activeq = &base->activequeues[i];
c = event_process_active_single_queue(base, activeq);
if (c < 0)
return -1;
else if (c > 0)
break; /* Processed a real event; do not
* consider lower-priority events */
/* If we get here, all of the events we processed
* were internal. Continue. */
}
}
event_process_deferred_callbacks(&base->defer_queue,&base->event_break);
return c;
}
event_process_active_single_queue
{
从头到尾:
如果是EV_PERSIST: 从active队列移出;否则,del_internel,从各个队列移出。
switch(ev_closure):
SIGNAL: event_signal_closure();
PERSIST: event_persist_closure();(从新调度这个event,并调用callback)
NONE: 释放掉锁,调用callback;
查看是否需要event_break;
}
- static int
- event_process_active_single_queue(struct event_base *base,
- struct event_list *activeq)
- {
- struct event *ev;
- int count = 0;
- EVUTIL_ASSERT(activeq != NULL);
- for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
- if (ev->ev_events & EV_PERSIST)
- event_queue_remove(base, ev, EVLIST_ACTIVE);
- else
- event_del_internal(ev);
- if (!(ev->ev_flags & EVLIST_INTERNAL))
- ++count;
- event_debug((
- "event_process_active: event: %p, %s%scall %p",
- ev,
- ev->ev_res & EV_READ ? "EV_READ " : " ",
- ev->ev_res & EV_WRITE ? "EV_WRITE " : " ",
- ev->ev_callback));
- switch (ev->ev_closure) {
- case EV_CLOSURE_SIGNAL:
- event_signal_closure(base, ev);
- break;
- case EV_CLOSURE_PERSIST:
- event_persist_closure(base, ev);
- break;
- default:
- case EV_CLOSURE_NONE:
- EVBASE_RELEASE_LOCK(base, th_base_lock);
- (*ev->ev_callback)(
- (int)ev->ev_fd, ev->ev_res, ev->ev_arg);
- break;
- }
- EVBASE_ACQUIRE_LOCK(base, th_base_lock);
- if (base->event_break)
- return -1;
- }
- return count;
- }
static int
event_process_active_single_queue(struct event_base *base,
struct event_list *activeq)
{
struct event *ev;
int count = 0;
EVUTIL_ASSERT(activeq != NULL);
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
if (ev->ev_events & EV_PERSIST)
event_queue_remove(base, ev, EVLIST_ACTIVE);
else
event_del_internal(ev);
if (!(ev->ev_flags & EVLIST_INTERNAL))
++count;
event_debug((
"event_process_active: event: %p, %s%scall %p",
ev,
ev->ev_res & EV_READ ? "EV_READ " : " ",
ev->ev_res & EV_WRITE ? "EV_WRITE " : " ",
ev->ev_callback));
switch (ev->ev_closure) {
case EV_CLOSURE_SIGNAL:
event_signal_closure(base, ev);
break;
case EV_CLOSURE_PERSIST:
event_persist_closure(base, ev);
break;
default:
case EV_CLOSURE_NONE:
EVBASE_RELEASE_LOCK(base, th_base_lock);
(*ev->ev_callback)(
(int)ev->ev_fd, ev->ev_res, ev->ev_arg);
break;
}
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (base->event_break)
return -1;
}
return count;
}
有时间仔细看看libev。粗看了一眼,那源代码实在风骚!