一、事件注册-event_add
1、将事件添加到等待事件中去,需要注意的是,event_add在event_new或者event_assign之后执行,即添加的事件必须是经过基本初始化过后的事件;
2、此处添加的事件包括IO事件、信号事件、定时事件,根据事件申请时设置的事件类型决定添加的流程;
3、超时控制包括两种方式:
(1)最小堆:时间超时时间存储在最小堆,每次执行超时任务都从最小堆堆顶取任务执行
(2)最小堆+公用超时队列:相同超时的任务存储在同一个超时队列,每一个超时队列的队首事件存储在最小堆,每次执行超时任务时都从最小堆堆顶取任务执行,然后遍历执行该任务所在公用超时队列中的所有超时任务。
/**
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 elapesed. If atimeout 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()
*/
// 将事件增加到一系列等待的事件中;
// 函数event_add规划以下事情:当event_assign或者event_new函数
// 创建’ev’时指定的条件发生时,或者超时时间达到时,事件’ev’的执行情况。
// 如果超时时间为NULL,那么没有超时事件发生,只能等匹配的事件发生时才会调用回调函数;
// 参数ev中的事件必须已经通过event_assign或者event_new进行过初始化了,而且只能当事件不再处于
// 等待状态时才能调用event_assign或者event_new函数。
// 如果ev参数中的事件有特定的超时时间,调用event_add时如果指定tc不为NULL的话,就会替代老的超时时间;
// 参数 ev:通过event_assign或者event_new初始化过的事件
// 参数timeout:等待事件执行的最长时间,如果NULL则从来不会超时,即永久等待
// 成功则返回0,失败则-1
// 相关查看event_del,event_assign,event_new
int
event_add(struct event *ev, const struct timeval *tv)
{
int res;
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_nolock实现的,下文会分析
res = event_add_nolock_(ev, tv, 0);
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
return (res);
}
二、事件注册内部实现函数-event_add_nolock_
此函数真正实现将事件添加到event_base的等待列表中。
/* 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));
// 已经处于结束状态的事件再次添加会报错
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. */
// 如果主线程当前正在执行信号事件的回调函数,同时又不在主线程,则
// 需要等待回调函数执行完毕才能继续添加事件,或者可能会在
// ev_ncalls和ev_pncalls上产生竞争。
#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
// 如果事件类型是IO事件/信号事件,同时事件状态不是已经插入/激活/下一次激活状态,
// 则根据事件类型将事件添加到不同的映射表或者队列中
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
// 如果事件是IO事件,则将事件插入到IO事件与文件描述符的映射表中
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
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);
// 如果上述添加行为正确,则将事件插入到event_base的事件列表中
if (res != -1)
event_queue_insert_inserted(base, ev);
// 如果上述添加行为正确,则设置通知主线程的标志,因为已经添加了新事件,
// 防止1)优先级高的事件被优先级低的事件倒挂,2)防止主线程忙等,会通知主线程有新事件
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;
#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.
*/
// 对于持久化的定时事件,需要记住超时时间,并重新注册事件
// 如果tv_is_absolute设置,则事件超时时间就等于输入时间参数
if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
ev->ev_io_timeout = *tv;
// 如果没有使用USE_REINSERT_TIMEOUT,则当事件处于超时状态时,需要从队列中移除事件
// 因为同样的事件不能重新插入,所以当一个事件已经处于超时状态时,为防止执行,需要先移除后插入
#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
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
}
// 将此事件的回调函数从激活队列中移除
event_queue_remove_active(base, event_to_event_callback(ev));
}
// 获取base中的缓存时间
gettime(base, &now);
// 检查base是否使用了公用超时队列机制
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
// 1)如果设置绝对超时时间,则设置时间超时时间为输入时间参数
// 2)如果使用的公用超时队列机制,则根据当前base中时间和输入超时时间间隔计算出时间超时时间,并对超时时间进行公用超时掩码计算
// 3)如果是其他情况,则直接根据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: 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
// event_queue_reinsert_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);
// 如果当前事件是公用超时队列的第一个事件,则因此需要将此超时事件插入最小堆
// 解释:公用超时队列机制:处于同一个公用超时队列中的所有事件具有相同的超时控制,因此只需要将公用超时队列
// 的第一个事件插入最小堆,当超时触发时,可以通过遍历公用超时队列获取同样的超时事件。
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))
evthread_notify_base(base);
event_debug_note_add_(ev);
return (res);
}
三、IO事件注册:evmap_io_add_
因为同一个文件描述符上可能会注册多个IO事件,因此base内部维护一个文件描述符和事件之间的映射表,此函数完成将事件和文件描述符映射。
/* return -1 on error, 0 on success if nothing changed in the event backend,
* and 1 on success if something did. */
// 如果没有任何改变的话,则返回0;如果某些东西改变了,则返回1;错误则返回-1
int
evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
{
// 后台方法
const struct eventop *evsel = base->evsel;
// IO事件和文件描述符的映射
struct event_io_map *io = &base->io;
struct evmap_io *ctx = NULL;
int nread, nwrite, nclose, retval = 0;
short res = 0, old = 0;
struct event *old_ev;
EVUTIL_ASSERT(fd == ev->ev_fd);
if (fd < 0)
return 0;
// 如果不使用EVMAP_USE_HT,即非win32下
#ifndef EVMAP_USE_HT
// 如果文件描述符大于项目数,则需要为新描述符创建空间
if (fd >= io->nentries) {
// 此函数为新添加的fd开辟空间,io中entries是二级指针,存放的是具体entry的地址,为了达到快速查找的目的,
// 此处申请的空间大小为最大的fd,fd存放的位置就是相对于entries起始位置的便宜,即将fd作为二级指针的偏移量
// 因此可能会浪费一些空间,通过空间换时间来提高查找速度
if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
return (-1);
}
#endif
// 如果不使用EVMAP_USE_HT的情况下,
// #define GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len) \
// GET_SIGNAL_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)
// 获取IO映射的槽以及存储的环境变量
// 其实就是根据fd,从evmap_io中获取该fd上注册的事件集合,因为同一个fd上可以注册多个事件。
GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
evsel->fdinfo_len);
// 获取原有事件的读事件个数、写事件个数、关闭事件个数
nread = ctx->nread;
nwrite = ctx->nwrite;
nclose = ctx->nclose;
// old变量用来存储原有事件类型
// 如果读、写、关闭事件个数不为0,则具有该事件类型
if (nread)
old |= EV_READ;
if (nwrite)
old |= EV_WRITE;
if (nclose)
old |= EV_CLOSED;
// res变量保存当前新增的事件类型,注意是新增的,而不是所有的
// 如果当前事件类型包含读事件,则如果原来没有读事件类型,则将新增读事件类型保存到res中;
// 如果当前事件类型包含写事件,则如果原来没有写事件类型,则将新增写事件类型保存到res中;
// 如果当前事件类型包含关闭事件,则如果原来没有关闭事件类型,则将新增关闭事件类型保存到res中;
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 (ev->ev_events & EV_CLOSED) {
if (++nclose == 1)
res |= EV_CLOSED;
}
// 如果读、写、关闭事件注册次数大于65535,则报错
if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
event_warnx("Too many events reading or writing on fd %d",
(int)fd);
return -1;
}
if (EVENT_DEBUG_MODE_IS_ON() &&
(old_ev = LIST_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;
ctx->nclose = (ev_uint16_t) nclose;
// 将事件插入到环境变量的队列中,从头部插入
LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
return (retval);
}
四、信号事件注册:evmap_signal_add_
由于同一个信号上可能会注册多个事件,因此,base内部会维护一份信号到事件的映射表,此函数完成事件到信号的映射
int
evmap_signal_add_(struct event_base *base, int sig, struct event *ev)
{
// 获取信号事件处理的后台方法以及信号与事件映射的map
const struct eventop *evsel = base->evsigsel;
struct event_signal_map *map = &base->sigmap;
struct evmap_signal *ctx = NULL;
// 如果注册的信号大于映射表中的项目个数,则需要为新注册信号申请空间
if (sig >= map->nentries) {
// 此函数为新添加的sig开辟空间,io中entries是二级指针,存放的是具体entry的地址,为了达到快速查找的目的,
// 此处申请的空间大小为最大的sig, sig存放的位置就是相对于entries起始位置的便宜,即将sig作为二级指针的偏移量
// 因此可能会浪费一些空间,通过空间换时间来提高查找速度
if (evmap_make_space(
map, sig, sizeof(struct evmap_signal *)) == -1)
return (-1);
}
// 根据信号获取信号与事件映射的环境变量ctx
GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
base->evsigsel->fdinfo_len);
// 如果此信号下映射的事件为空,则将信号与文件描述符注册到后台方法中
// evsel->add实际上调用的是base->evsigsel->add函数,查看evsig_init函数中初始化过程
// 得知,实际上调用的是evsig_add函数
// 这里就把外部信号添加到信号捕捉函数中了
if (LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
== -1)
return (-1);
}
// 将新注册的信号事件插入到环境变量头部
LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);
return (1);
}
五、事件删除event_del
这里只是删除,并不是释放事件空间
/**
Remove an event from the set of monitored events.
The function event_del() will cancel the event in the argument ev. If the
event has already executed or has never been added the call will have no
effect.
@param ev an event struct to be removed from the working set
@return 0 if successful, or -1 if an error occurred
@see event_add()
*/
// 从一系列监听的事件中移除事件,函数event_del将取消参数ev中的event。如果event
// 已经执行或者还没有添加成功,则此调用无效
int
event_del(struct event *ev)
{
return event_del_(ev, EVENT_DEL_AUTOBLOCK);
}
六、内部删除事件函数:event_del_
static int
event_del_(struct event *ev, int blocking)
{
int res;
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);
res = event_del_nolock_(ev, blocking);
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
return (res);
}
七、event_del_nolock_
事件删除的内部实现,不加锁;
需要根据事件状态判断是否能够执行删除行为;
需要根据事件类型决定删除的具体操作;
/** Helper for event_del: always called with th_base_lock held.
*
* "blocking" must be one of the EVENT_DEL_{BLOCK, NOBLOCK, AUTOBLOCK,
* EVEN_IF_FINALIZING} values. See those for more information.
*/
int
event_del_nolock_(struct event *ev, int blocking)
{
struct event_base *base;
int res = 0, notify = 0;
event_debug(("event_del: %p (fd "EV_SOCK_FMT"), callback %p",
ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_callback));
/* An event without a base has not been added */
if (ev->ev_base == NULL)
return (-1);
EVENT_BASE_ASSERT_LOCKED(ev->ev_base);
// 如果事件已经处于结束中的状态,则不需要重复删除
if (blocking != EVENT_DEL_EVEN_IF_FINALIZING) {
if (ev->ev_flags & EVLIST_FINALIZING) {
/* XXXX Debug */
return 0;
}
}
base = ev->ev_base;
EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
/* See if we are just active executing this event in a loop */
// 如果是信号事件,同时信号触发事件不为0,则放弃执行这些回调函数
if (ev->ev_events & EV_SIGNAL) {
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
}
// 如果事件状态处于已超时状态
// 从来不需要因为一个已删除的超时事件而通知主线程:即使我们不通知主线程,可能会发生的就是调度loop会过早的唤醒;
// 但是通知主线程的点是尽可能早地唤醒调度loop,因此即使通知也不会获得更好的性能。
// 从超时队列中移除事件
if (ev->ev_flags & EVLIST_TIMEOUT) {
/* NOTE: We never need to notify the main thread because of a
* deleted timeout event: all that could happen if we don't is
* that the dispatch loop might wake up too early. But the
* point of notifying the main thread _is_ to wake up the
* dispatch loop early anyway, so we wouldn't gain anything by
* doing it.
*/
event_queue_remove_timeout(base, ev);
}
// 如果事件正处于激活状态,则需要将事件的回调函数从激活队列中删除
// 如果事件正处于下一次激活状态,则需要将事件的回调函数从下一次激活队列中删除
if (ev->ev_flags & EVLIST_ACTIVE)
event_queue_remove_active(base, event_to_event_callback(ev));
else if (ev->ev_flags & EVLIST_ACTIVE_LATER)
event_queue_remove_active_later(base, event_to_event_callback(ev));
// 如果事件正处于已插入状态,则需要将事件从已插入状态队列中删除
if (ev->ev_flags & EVLIST_INSERTED) {
event_queue_remove_inserted(base, ev);
// 如果事件是IO事件,则将事件从IO映射中删除
// 如果是信号事件,则将信号从信号映射删除
// 如果删除正确,则需要通知主线程
if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
res = evmap_io_del_(base, ev->ev_fd, ev);
else
res = evmap_signal_del_(base, (int)ev->ev_fd, ev);
if (res == 1) {
/* evmap says we need to notify the main thread. */
notify = 1;
res = 0;
}
}
/* 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_del_(ev);
/* If the main thread is currently executing this event's callback,
* and we are not the main thread, then we want to wait until the
* callback is done before returning. That way, when this function
* returns, it will be safe to free the user-supplied argument.
*/
#ifndef EVENT__DISABLE_THREAD_SUPPORT
if (blocking != EVENT_DEL_NOBLOCK &&
base->current_event == event_to_event_callback(ev) &&
!EVBASE_IN_THREAD(base) &&
(blocking == EVENT_DEL_BLOCK || !(ev->ev_events & EV_FINALIZE))) {
++base->current_event_waiters;
EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
}
#endif
return (res);
}
八、从队列中 删除超时事件event_queue_remove_timeout
static void
event_queue_remove_timeout(struct event_base *base, struct event *ev)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_TIMEOUT))) {
event_errx(1, "%s: %p(fd "EV_SOCK_FMT") not on queue %x", __func__,
ev, EV_SOCK_ARG(ev->ev_fd), EVLIST_TIMEOUT);
return;
}
// base中事件数量-1,同时设置事件状态为非超时状态
DECR_EVENT_COUNT(base, ev->ev_flags);
ev->ev_flags &= ~EVLIST_TIMEOUT;
// 如果使用了公用超时队列,则找到待删除事件所在公用超时队列,
// 然后删除公用超时队列
// 如果使用最小堆,则将事件从最小堆删除即可
if (is_common_timeout(&ev->ev_timeout, base)) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
TAILQ_REMOVE(&ctl->events, ev,
ev_timeout_pos.ev_next_with_common_timeout);
} else {
min_heap_erase_(&base->timeheap, ev);
}
}
九、从队列中删除激活事件event_queue_remove_active
实际上是从base维护的激活事件列表中删除事件的回调函数;
static void
event_queue_remove_active(struct event_base *base, struct event_callback *evcb)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (EVUTIL_FAILURE_CHECK(!(evcb->evcb_flags & EVLIST_ACTIVE))) {
event_errx(1, "%s: %p not on queue %x", __func__,
evcb, EVLIST_ACTIVE);
return;
}
// base中事件个数-1,同时设置事件回调函数状态为非激活状态
// 同时base中激活事件个数-1
DECR_EVENT_COUNT(base, evcb->evcb_flags);
evcb->evcb_flags &= ~EVLIST_ACTIVE;
base->event_count_active--;
// 将base中激活队列中该事件的回调函数删除即可
TAILQ_REMOVE(&base->activequeues[evcb->evcb_pri],
evcb, evcb_active_next);
}
十、从下一次激活队列中删除事件回调函数 event_queue_remove_active_later
实际上是从base维护的下一次激活列表中删除事件的回调函数
static void
event_queue_remove_active_later(struct event_base *base, struct event_callback *evcb)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (EVUTIL_FAILURE_CHECK(!(evcb->evcb_flags & EVLIST_ACTIVE_LATER))) {
event_errx(1, "%s: %p not on queue %x", __func__,
evcb, EVLIST_ACTIVE_LATER);
return;
}
// base中事件个数-1,同时事件回调函数状态设置为非下一次激活状态
// 同时base中激活事件个数-1
DECR_EVENT_COUNT(base, evcb->evcb_flags);
evcb->evcb_flags &= ~EVLIST_ACTIVE_LATER;
base->event_count_active--;
// 将base中下一次激活队列中删除该事件的回调函数即可
TAILQ_REMOVE(&base->active_later_queue, evcb, evcb_active_next);
}
十一、从IO事件与文件描述符的映射中删除事件
/** Remove an IO event (some combination of EV_READ or EV_WRITE) to an
event_base's list of events on a given file descriptor, and tell the
underlying eventops about the fd if its state has changed.
@param base the event_base to operate on.
@param fd the file descriptor corresponding to ev.
@param ev the event to remove.
*/
// 删除IO事件;并告诉潜在的eventops,输入的fd状态已经改变了;
// base:操作的event_base
// fd:和事件相关的文件描述符
// ev:要移除的事件
int
evmap_io_del_(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;
int nread, nwrite, nclose, retval = 0;
short res = 0, old = 0;
if (fd < 0)
return 0;
EVUTIL_ASSERT(fd == ev->ev_fd);
#ifndef EVMAP_USE_HT
if (fd >= io->nentries)
return (-1);
#endif
根据fd获取相关事件的ctx
GET_IO_SLOT(ctx, io, fd, evmap_io);
// 获取当前fd上注册的读、写、关闭事件个数
nread = ctx->nread;
nwrite = ctx->nwrite;
nclose = ctx->nclose;
// 使用old变量保存原有的事件类型
if (nread)
old |= EV_READ;
if (nwrite)
old |= EV_WRITE;
if (nclose)
old |= EV_CLOSED;
// 使用res保存删除的事件类型
if (ev->ev_events & EV_READ) {
if (--nread == 0)
res |= EV_READ;
EVUTIL_ASSERT(nread >= 0);
}
if (ev->ev_events & EV_WRITE) {
if (--nwrite == 0)
res |= EV_WRITE;
EVUTIL_ASSERT(nwrite >= 0);
}
if (ev->ev_events & EV_CLOSED) {
if (--nclose == 0)
res |= EV_CLOSED;
EVUTIL_ASSERT(nclose >= 0);
}
// 如果删除的事件不为空,则调用evsel->del删除事件
// 实际上是调用的后台方法的del方法,这是在event_base创建时指定的
if (res) {
void *extra = ((char*)ctx) + sizeof(struct evmap_io);
if (evsel->del(base, ev->ev_fd, old, res, extra) == -1) {
retval = -1;
} else {
retval = 1;
}
}
ctx->nread = nread;
ctx->nwrite = nwrite;
ctx->nclose = nclose;
LIST_REMOVE(ev, ev_io_next);
return (retval);
}
十二、从信号事件与信号的映射表中删除事件evmap_signal_del_
int
evmap_signal_del_(struct event_base *base, int sig, struct event *ev)
{
const struct eventop *evsel = base->evsigsel;
struct event_signal_map *map = &base->sigmap;
struct evmap_signal *ctx;
if (sig >= map->nentries)
return (-1);
// 根据sig获取相关ctx
GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
// 删除事件
LIST_REMOVE(ev, ev_signal_next);
// 如果信号上注册的事件为空,则需要从信号的后台处理方法上删除
// 实际上调用的是evsig_del函数,这是在evsig_init函数中指定的
if (LIST_FIRST(&ctx->events) == NULL) {
if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
return (-1);
}
return (1);
}