event_add、event_del两个函数分别是使event生效和失效的,下面就来看一下两个函数的实现。

event_add

 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 -;
} EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); res = event_add_nolock_(ev, tv, ); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); return (res);
} /* 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 */
int
event_add_nolock_(struct event *ev, const struct timeval *tv,
int tv_is_absolute)
{
struct event_base *base = ev->ev_base;
int res = ;
int notify = ; 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 (-);
} /*
* 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,
+ min_heap_size_(&base->timeheap)) == -)
return (-); /* 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 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);
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add_(base, (int)ev->ev_fd, ev);
if (res != -)
event_queue_insert_inserted(base, ev);
if (res == ) {
/* evmap says we need to notify the main thread. */
notify = ;
res = ;
}
} /*
* we should change the timeout state only if the previous event
* addition succeeded.
*/
if (res != - && 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 (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
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = ;
}
} 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
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 = ;
else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
evutil_timercmp(&top->ev_timeout, &now, <))
notify = ;
}
} /* if we are not in the right thread, we need to wake up the loop */
if (res != - && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base); event_debug_note_add_(ev); return (res);
}

这里以epoll作为后端来举例分析event_add函数的调用流程:

event_del

 int
event_del(struct event *ev)
{
return event_del_(ev, EVENT_DEL_AUTOBLOCK);
} 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 -;
} 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);
} /** 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 = , notify = ; 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 (-); EVENT_BASE_ASSERT_LOCKED(ev->ev_base); if (blocking != EVENT_DEL_EVEN_IF_FINALIZING) {
if (ev->ev_flags & EVLIST_FINALIZING) {
/* XXXX Debug */
return ;
}
} /* 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 we start removing the event. That way,
* when this function returns, it will be safe to free the
* user-supplied argument. */
base = ev->ev_base;
#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 EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL)); /* See if we are just active executing this event in a loop */
if (ev->ev_events & EV_SIGNAL) {
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = ;
}
} 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);
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 == ) {
/* evmap says we need to notify the main thread. */
notify = ;
res = ;
}
} /* if we are not in the right thread, we need to wake up the loop */
if (res != - && notify && EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base); event_debug_note_del_(ev); return (res);
}

这里以epoll作为后端来分析event_del的调用流程:

结论:

到这里event_add、event_del函数就分析完了,这两个函数的功能就是使事件生效和失效,以epoll作为后端举例,最后都会调用epoll_ctl来修改事件,libevent实现的很复杂,是因为它考虑到效率的问题,关于libevent如何保证了libevent的高效,这个待之后彻底理解了libevent的设计之后再来分析。

libevent源码分析:event_add、event_del的更多相关文章

  1. Libevent源码分析—event_add()

    接下来就是将已经初始化的event注册到libevent的事件链表上,通过event_add()来实现,源码位于event.c中. event_add() 这个函数主要完成了下面几件事: 1.将eve ...

  2. 【转】libevent源码分析

    libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...

  3. Libevent源码分析 (1) hello-world

    Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...

  4. libevent源码分析

    这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络 ...

  5. Libevent源码分析系列【转】

    转自:https://www.cnblogs.com/zxiner/p/6919021.html 1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了 ...

  6. Libevent源码分析系列

    1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了,然后去看底层相应的源码,这样比较有条理,自上向下掌握.下面用libevent库写个程序,每隔1秒 ...

  7. Libevent源码分析—event_base_dispatch()

    我们知道libevent是一个Reactor模式的事件驱动的网络库.   到目前为止,我们已经看了核心的event和event_base结构体的源码,看了初始化这两个结构体的源码,看了注册event的 ...

  8. libevent源码分析一--io事件响应

    这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1.  select l ...

  9. libevent源码分析之信号处理

    新看看官方demo的libevent如何使用信号 int called = 0; static void signal_cb(int fd, short event, void *arg) { str ...

随机推荐

  1. ACM经验分享[转]

    明确规则 规则:以最少的时间过题 (这意味着0ms与1000ms是一样的) 了解规则,善用规则 虽然这个题我不会但是AC是没有问题的 --ACRush 大力出奇迹 学会对拍数据,准备好对拍脚本:测试很 ...

  2. 实现仿UC浏览器首页下拉动画

    经常用UC看到首页有这么一个动画,就仿造写了一下. 实现分析 1.画曲线的动画 这个一眼看去就想到用贝塞尔曲线画,来看贝塞尔曲线方法,给出两个定点,和一个控制点就可以画. CGContextAddQu ...

  3. 如何在windows server 2008 部署asp.net mvc

    我们做好的asp.net mvc网站,要部署到windows server 2008(IIS7.0)网站,首先要安装好.net framework 4.0,开发工具VS2010,VS2012,VS20 ...

  4. PHP位运算用途

    在实际应用中可以做用户权限的应用 我这里说到的权限管理办法是一个普遍采用的方法,主要是使用到”位运行符”操作,& 位与运算符.| 位或运行符.参与运算的如果是10进制数,则会被转换至2进制数参 ...

  5. 【转载并整理】mysql分页方法

    http://blog.csdn.net/bestcleaner/article/details/52993468

  6. tomcat启动报错 java.lang.ClassNotFoundException: org.apache.jsp.index_jsp

    项目运行一直很平稳,但是换了tomcat之后打开jsp网页时就报错,描述如下: 1. 错误描述 打开jsp网页时报错 java.lang.NullPointerException     org.ap ...

  7. U811.1接口EAI系列之一-通用访问EAI方法--统一调用EAI公共方法--VB语言

    1.现在做的项目是关于业务系统与U811.1的接口项目. 2.那么就需要调整通过EAI调用生成U8业务单据. 3.下面就一个通用的向U8-EAI传递XML的通用方法 4.肯定有人会问怎么还用VB调用呢 ...

  8. 《Deep Learning》(深度学习)中文版 开发下载

    <Deep Learning>(深度学习)中文版开放下载   <Deep Learning>(深度学习)是一本皆在帮助学生和从业人员进入机器学习领域的教科书,以开源的形式免费在 ...

  9. spring注解方式实现定时器

    1.Spring的配置: <beans xmlns:task="http://www.springframework.org/schema/task" xsi:schemaL ...

  10. ELK logstash邮件报警

    这个方法有一个问题就是我这边不能给我们公司的邮箱发邮件.还有就是我们有两个邮箱一个是腾讯企业邮箱,还有一个就是我们的集团邮箱 使用下面的这个方法是不能给我们的集团邮箱发邮件的.第二个问题就是这个方法给 ...