evconnlistener_new_bind中还差一个event_assign函数没看,现在来看一下:
先回一下函数定义:
/**
准备要添加的新的、已分配的事件结构。
函数event_assign()准备事件结构ev,以便在将来调用event_ add()和eventU del()时使用。
与event_new()不同,它本身不分配内存:它要求您已经分配了一个struct事件,可能是在堆上。
这样做通常会使代码取决于事件结构的大小,从而导致与Libevent的未来版本不兼容。
避免此问题的最简单方法就是使用event_new()和event_ free()。
要证明代码的未来性,一种稍微困难的方法是使用event_get_struct_event_ size()来确定运行时所需的事件大小。
请注意,在活动或挂起的事件上调用此函数是不安全的。
这样做会破坏Libevent中的内部数据结构,并导致奇怪的、难以诊断的错误!
此函数的参数及其生成的事件行为与event_new()相同。
@param ev:要修改的事件结构
@param base ev应连接到的事件库。
@param fd:要监视的文件描述符
@参数事件:需要监视的事件;可以是EV_ READ和/或EV_
@事件发生时要调用的param回调函数
@param callback_arg:要传递给回调函数的参数
@如果成功,则返回0;如果参数无效,则返回-1。
@请参见event_new()、event_ add()、event_del()、event_base_once()、evelt_get_struct_
*/
EVENT2_EXPORT_SYMBOL
int event_assign(struct event *, struct event_base *, evutil_socket_t, short, event_callback_fn, void *);
该函数主要是给event结构体初始化赋值,绑定event_base、回调函数和回调函数参数、文件描述符以及标志位,中间判断持续化事件。(ps:如果这个event是用来监听一个信号的,那么就不能让这个event监听读或者写事件。原因是其与信号event的实现方法相抵触。)
然后看下怎么实现的
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;//NULL
if (arg == &event_self_cbarg_ptr_)//event_self_cbarg_ptr_ NULL
arg = ev;
if (!(events & EV_SIGNAL))
event_debug_assert_socket_nonblocking_(fd);//判断fd是否合法
event_debug_assert_not_added_(ev);//判断ev是否合法,没有add(ps:请注意,在活动或挂起的事件上调用event_assign是不安全的)
//初始化参数 绑定base,回调函数,回调函数参数,文件描述符以及标志位,中间判断持续化事件
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;
//p判断监听事件、不允许监听信号后还监听读写
if (events & EV_SIGNAL) {
if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) {
event_warnx("%s: EV_SIGNAL is not compatible with "
"EV_READ, EV_WRITE or EV_CLOSED", __func__);
return -1;
}
ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
} else {
if (events & EV_PERSIST) {
evutil_timerclear(&ev->ev_io_timeout);
ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;
} else {
ev->ev_closure = EV_CLOSURE_EVENT;
}
}
min_heap_elem_init_(ev);//初始化最小堆(置-1)
if (base != NULL) {
/* by default, we put new events into the middle priority */
//默认情况下,我们将新事件置于中间优先级
ev->ev_pri = base->nactivequeues / 2;
}
//记录ev现在已设置(即准备添加)
event_debug_note_setup_(ev);
return 0;
}
然后就是add一下:
/**
Re-enable an evconnlistener that has been disabled.
//重新启用已禁用的evconnlistener。
*/
EVENT2_EXPORT_SYMBOL
int evconnlistener_enable(struct evconnlistener *lev);
看一下是怎么做的:
int
evconnlistener_enable(struct evconnlistener *lev)
{
int r;
LOCK(lev);
lev->enabled = 1;
if (lev->cb)
r = lev->ops->enable(lev);
else
r = 0;
UNLOCK(lev);
return r;
}
合理,改变状态值,调用enable函数。那让我们看一下enable函数:
static int
event_listener_enable(struct evconnlistener *lev)
{
//辅助宏。如果我们知道给定指针指向结构中的字段,则返回指向结构本身的指针。
//根据对外提供的evconnlistener获取evconnlistener_event的地址,本来强制转换即可,但是为了可移植性
struct evconnlistener_event *lev_e =
EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
return event_add(&lev_e->listener, NULL);
}
继续,里面调用了event_add函数,老规矩,先看函数定义:
/**
Add an event to the set of pending events.
The function event_add() schedules the execution of the event 'ev' when the
condition specified by event_assign() or event_new() occurs, or when the time
specified in timeout has elapsed. If a timeout is NULL, no timeout
occurs and the function will only be
called if a matching event occurs. The event in the
ev argument must be already initialized by event_assign() or event_new()
and may not be used
in calls to event_assign() until it is no longer pending.
If the event in the ev argument already has a scheduled timeout, calling
event_add() replaces the old timeout with the new one if tv is non-NULL.
@param ev an event struct initialized via event_assign() or event_new()
@param timeout the maximum amount of time to wait for the event, or NULL
to wait forever
@return 0 if successful, or -1 if an error occurred
@see event_del(), event_assign(), event_new()
*/
EVENT2_EXPORT_SYMBOL
int event_add(struct event *ev, const struct timeval *timeout);
百度翻译一下:
/**
将事件添加到挂起事件集。
函数event_add()在event_assign()或event_ new()指定的条件发生时,
或在超时中指定的时间已过时,调度事件“ev”的执行。
如果超时为NULL,则不会发生超时,并且仅当发生匹配事件时才会调用该函数。
ev参数中的事件必须已经由event_assign()或event_ new()初始化,
并且在不再挂起之前,不得用于调用event_assign()。
如果ev参数中的事件已经有一个计划的超时,如果tv为非空,
则调用event_add()将旧的超时替换为新的超时。
@param ev:通过event_assign()或event_ new()初始化的事件结构
@param timeout等待事件的最长时间,或NULL永远等待
@如果成功,则返回0;如果发生错误,则返回-1
@请参见event_del()、event_ assign()、event_new()
*/
然后看函数实现:
int
event_add(struct event *ev, const struct timeval *tv)
{
int res;
//检查是否已经绑定了ev_base,(event_assign中赋值)
if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
event_warnx("%s: event has no event_base set.", __func__);
return -1;
}
//加锁
EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
//用于添加事件的实现函数。其工作原理与event_ add类似,
//除了:1)它要求我们有锁。2) 如果设置了tv_ is_,
//我们将tv视为绝对时间,而不是添加到当前时间的间隔
res = event_add_nolock_(ev, tv, 0);
//解锁
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
return (res);
}
检查、加锁,然后功能由event_add_nolock_函数完成,该函数实现如下:
/* Implementation function to add an event. Works just like event_add,
* except: 1) it requires that we have the lock. 2) if tv_is_absolute is set,
* we treat tv as an absolute time, not as an interval to add to the current
* time */
/*
用于添加事件的实现函数。它的工作原理与event_add类似,除了:
1) 它要求我们有锁。
2) 如果设置了tv_is_absolute, 我们将tv视为绝对时间,而不是添加到当前时间的间隔
*/
int
event_add_nolock_(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 "EV_SOCK_FMT"), %s%s%s%scall %p",
ev,
EV_SOCK_ARG(ev->ev_fd),
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback));
EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));//判断标签合不合法,确保event的flags是给定的几种的合法组合
//已经处于结束状态的事件再次添加会报错
if (ev->ev_flags & EVLIST_FINALIZING) {
/* XXXX debug */
return (-1);
}
/*
* 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 the main thread is currently executing a signal event's
* callback, and we are not the main thread, then we want to wait
* until the callback is done before we mess with the event, or else
* we can race on ev_ncalls and ev_pncalls below. */
/*
如果主线程当前正在执行信号事件的回调,而我们不是主线程,那么我们希望等到回调完成后再处理事件。
*/
#ifndef EVENT__DISABLE_THREAD_SUPPORT
if (base->current_event == event_to_event_callback(ev) &&
(ev->ev_events & EV_SIGNAL)
&& !EVBASE_IN_THREAD(base)) {
++base->current_event_waiters;
EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
}
#endif
//按照ev_flags,添加io或信号触发
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
res = evmap_io_add_(base, ev->ev_fd, ev);//将IO事件(读、写)添加到给定文件描述符上的event_ base事件列表中,如果fd的状态已更改,则告知底层eventops
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add_(base, (int)ev->ev_fd, ev);//这些函数的行为方式与IO相同,只是它们处理的是信号而不是FD。信号在任何地方都使用线性映射;FD使用线性映射或哈希表
if (res != -1)
event_queue_insert_inserted(base, ev);//将事件处理器插入到注册事件队列
if (res == 1) {
/* evmap says we need to notify the main thread. */
//evmap说我们需要通知主线程。
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;
#ifdef USE_REINSERT_TIMEOUT
int was_common;
int old_timeout_idx;
#endif
/*
* for persistent timeout events, we remember the
* timeout value and re-add the event.
*
* If tv_is_absolute, this was already set.
*/
//对于持久超时事件,我们记住超时值并重新添加事件
//If tv_is_absolute 这已经设置好了。
if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
ev->ev_io_timeout = *tv;
#ifndef USE_REINSERT_TIMEOUT
//若该事件处理器已经被插入到通用定时器队列或时间堆当中,则先对其进行删除
if (ev->ev_flags & EVLIST_TIMEOUT) {
event_queue_remove_timeout(base, ev);
}
#endif
/* 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
*/
//看看我们是否只是在循环中执行此事件
//若事件处理器的回调函数正在执行当中,则设置 ev_ncalls 的值为 0,就可以退出回调函数的执行循环
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
}
//删除
event_queue_remove_active(base, event_to_event_callback(ev));
}
gettime(base, &now);
common_timeout = is_common_timeout(tv, base);用于判断应该将定时器插入到通用定时器队列还是时间堆当中
#ifdef USE_REINSERT_TIMEOUT
was_common = is_common_timeout(&ev->ev_timeout, base);
old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
#endif
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: event %p, timeout in %d seconds %d useconds, call %p",
ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));
//插入定时器
#ifdef USE_REINSERT_TIMEOUT
//根据 common_timeout 决定插入的位置是通用定时器队列还是时间堆
event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
#else
event_queue_insert_timeout(base, ev);
#endif
if (common_timeout) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
//若被插入的定时器是通用定时器队列的首元素,则调用 common_timeout_schedule
//将其转移到时间堆单中,这样可以使得时间堆和定时器链表的定时器得到相同的处理。
if (ev == TAILQ_FIRST(&ctl->events)) {
common_timeout_schedule(ctl, &now, ev);
}
} else {
struct event* top = NULL;
/* 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.
* We double check the timeout of the top element to
* handle time distortions due to system suspension.
*/
//看看最早的超时现在是否比以前早:如果是这样,我们需要告诉主线程比以前更早唤醒。
//我们仔细检查顶部元素的超时,以处理由于系统暂停而导致的时间扭曲。
if (min_heap_elt_is_top_(ev))
notify = 1;
//若定时器不位于堆顶,则取出堆顶的定时器,判断是否小于当前时间,如果是,则通知主线程
else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
evutil_timercmp(&top->ev_timeout, &now, <))
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))
//告诉当前运行event_loop for base(如果有)的线程,它需要停止在其调度函数中等待(如果有),并处理所有活动回调。
evthread_notify_base(base);
event_debug_note_add_(ev);
return (res);
}
添加了信号、io的映射,并初始化了定时器,将其添加至时间堆或通用定时器队列。