libevent源码分析:event_add、event_del

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

event_add

  1 int
  2 event_add(struct event *ev, const struct timeval *tv)
  3 {
  4     int res;
  5
  6     if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
  7         event_warnx("%s: event has no event_base set.", __func__);
  8         return -1;
  9     }
 10
 11     EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
 12
 13     res = event_add_nolock_(ev, tv, 0);
 14
 15     EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
 16
 17     return (res);
 18 }
 19
 20 /* Implementation function to add an event.  Works just like event_add,
 21  * except: 1) it requires that we have the lock.  2) if tv_is_absolute is set,
 22  * we treat tv as an absolute time, not as an interval to add to the current
 23  * time */
 24 int
 25 event_add_nolock_(struct event *ev, const struct timeval *tv,
 26     int tv_is_absolute)
 27 {
 28     struct event_base *base = ev->ev_base;
 29     int res = 0;
 30     int notify = 0;
 31
 32     EVENT_BASE_ASSERT_LOCKED(base);
 33     event_debug_assert_is_setup_(ev);
 34
 35     event_debug((
 36          "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p",
 37          ev,
 38          EV_SOCK_ARG(ev->ev_fd),
 39          ev->ev_events & EV_READ ? "EV_READ " : " ",
 40          ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
 41          ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",
 42          tv ? "EV_TIMEOUT " : " ",
 43          ev->ev_callback));
 44
 45     EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
 46
 47     if (ev->ev_flags & EVLIST_FINALIZING) {
 48         /* XXXX debug */
 49         return (-1);
 50     }
 51
 52     /*
 53      * prepare for timeout insertion further below, if we get a
 54      * failure on any step, we should not change any state.
 55      */
 56     if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
 57         if (min_heap_reserve_(&base->timeheap,
 58             1 + min_heap_size_(&base->timeheap)) == -1)
 59             return (-1);  /* ENOMEM == errno */
 60     }
 61
 62     /* If the main thread is currently executing a signal event‘s
 63      * callback, and we are not the main thread, then we want to wait
 64      * until the callback is done before we mess with the event, or else
 65      * we can race on ev_ncalls and ev_pncalls below. */
 66 #ifndef EVENT__DISABLE_THREAD_SUPPORT
 67     if (base->current_event == event_to_event_callback(ev) &&
 68         (ev->ev_events & EV_SIGNAL)
 69         && !EVBASE_IN_THREAD(base)) {
 70         ++base->current_event_waiters;
 71         EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
 72     }
 73 #endif
 74
 75     if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
 76         !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
 77         if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
 78             res = evmap_io_add_(base, ev->ev_fd, ev);
 79         else if (ev->ev_events & EV_SIGNAL)
 80             res = evmap_signal_add_(base, (int)ev->ev_fd, ev);
 81         if (res != -1)
 82             event_queue_insert_inserted(base, ev);
 83         if (res == 1) {
 84             /* evmap says we need to notify the main thread. */
 85             notify = 1;
 86             res = 0;
 87         }
 88     }
 89
 90     /*
 91      * we should change the timeout state only if the previous event
 92      * addition succeeded.
 93      */
 94     if (res != -1 && tv != NULL) {
 95         struct timeval now;
 96         int common_timeout;
 97 #ifdef USE_REINSERT_TIMEOUT
 98         int was_common;
 99         int old_timeout_idx;
100 #endif
101
102         /*
103          * for persistent timeout events, we remember the
104          * timeout value and re-add the event.
105          *
106          * If tv_is_absolute, this was already set.
107          */
108         if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
109             ev->ev_io_timeout = *tv;
110
111 #ifndef USE_REINSERT_TIMEOUT
112         if (ev->ev_flags & EVLIST_TIMEOUT) {
113             event_queue_remove_timeout(base, ev);
114         }
115 #endif
116
117         /* Check if it is active due to a timeout.  Rescheduling
118          * this timeout before the callback can be executed
119          * removes it from the active list. */
120         if ((ev->ev_flags & EVLIST_ACTIVE) &&
121             (ev->ev_res & EV_TIMEOUT)) {
122             if (ev->ev_events & EV_SIGNAL) {
123                 /* See if we are just active executing
124                  * this event in a loop
125                  */
126                 if (ev->ev_ncalls && ev->ev_pncalls) {
127                     /* Abort loop */
128                     *ev->ev_pncalls = 0;
129                 }
130             }
131
132             event_queue_remove_active(base, event_to_event_callback(ev));
133         }
134
135         gettime(base, &now);
136
137         common_timeout = is_common_timeout(tv, base);
138 #ifdef USE_REINSERT_TIMEOUT
139         was_common = is_common_timeout(&ev->ev_timeout, base);
140         old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
141 #endif
142
143         if (tv_is_absolute) {
144             ev->ev_timeout = *tv;
145         } else if (common_timeout) {
146             struct timeval tmp = *tv;
147             tmp.tv_usec &= MICROSECONDS_MASK;
148             evutil_timeradd(&now, &tmp, &ev->ev_timeout);
149             ev->ev_timeout.tv_usec |=
150                 (tv->tv_usec & ~MICROSECONDS_MASK);
151         } else {
152             evutil_timeradd(&now, tv, &ev->ev_timeout);
153         }
154
155         event_debug((
156              "event_add: event %p, timeout in %d seconds %d useconds, call %p",
157              ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));
158
159 #ifdef USE_REINSERT_TIMEOUT
160         event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
161 #else
162         event_queue_insert_timeout(base, ev);
163 #endif
164
165         if (common_timeout) {
166             struct common_timeout_list *ctl =
167                 get_common_timeout_list(base, &ev->ev_timeout);
168             if (ev == TAILQ_FIRST(&ctl->events)) {
169                 common_timeout_schedule(ctl, &now, ev);
170             }
171         } else {
172             struct event* top = NULL;
173             /* See if the earliest timeout is now earlier than it
174              * was before: if so, we will need to tell the main
175              * thread to wake up earlier than it would otherwise.
176              * We double check the timeout of the top element to
177              * handle time distortions due to system suspension.
178              */
179             if (min_heap_elt_is_top_(ev))
180                 notify = 1;
181             else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
182                      evutil_timercmp(&top->ev_timeout, &now, <))
183                 notify = 1;
184         }
185     }
186
187     /* if we are not in the right thread, we need to wake up the loop */
188     if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
189         evthread_notify_base(base);
190
191     event_debug_note_add_(ev);
192
193     return (res);
194 }

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

event_del

  1 int
  2 event_del(struct event *ev)
  3 {
  4     return event_del_(ev, EVENT_DEL_AUTOBLOCK);
  5 }
  6
  7 static int
  8 event_del_(struct event *ev, int blocking)
  9 {
 10     int res;
 11
 12     if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
 13         event_warnx("%s: event has no event_base set.", __func__);
 14         return -1;
 15     }
 16
 17     EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
 18
 19     res = event_del_nolock_(ev, blocking);
 20
 21     EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
 22
 23     return (res);
 24 }
 25
 26 /** Helper for event_del: always called with th_base_lock held.
 27  *
 28  * "blocking" must be one of the EVENT_DEL_{BLOCK, NOBLOCK, AUTOBLOCK,
 29  * EVEN_IF_FINALIZING} values. See those for more information.
 30  */
 31 int
 32 event_del_nolock_(struct event *ev, int blocking)
 33 {
 34     struct event_base *base;
 35     int res = 0, notify = 0;
 36
 37     event_debug(("event_del: %p (fd "EV_SOCK_FMT"), callback %p",
 38         ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_callback));
 39
 40     /* An event without a base has not been added */
 41     if (ev->ev_base == NULL)
 42         return (-1);
 43
 44     EVENT_BASE_ASSERT_LOCKED(ev->ev_base);
 45
 46     if (blocking != EVENT_DEL_EVEN_IF_FINALIZING) {
 47         if (ev->ev_flags & EVLIST_FINALIZING) {
 48             /* XXXX Debug */
 49             return 0;
 50         }
 51     }
 52
 53     /* If the main thread is currently executing this event‘s callback,
 54      * and we are not the main thread, then we want to wait until the
 55      * callback is done before we start removing the event.  That way,
 56      * when this function returns, it will be safe to free the
 57      * user-supplied argument. */
 58     base = ev->ev_base;
 59 #ifndef EVENT__DISABLE_THREAD_SUPPORT
 60     if (blocking != EVENT_DEL_NOBLOCK &&
 61         base->current_event == event_to_event_callback(ev) &&
 62         !EVBASE_IN_THREAD(base) &&
 63         (blocking == EVENT_DEL_BLOCK || !(ev->ev_events & EV_FINALIZE))) {
 64         ++base->current_event_waiters;
 65         EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
 66     }
 67 #endif
 68
 69     EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));
 70
 71     /* See if we are just active executing this event in a loop */
 72     if (ev->ev_events & EV_SIGNAL) {
 73         if (ev->ev_ncalls && ev->ev_pncalls) {
 74             /* Abort loop */
 75             *ev->ev_pncalls = 0;
 76         }
 77     }
 78
 79     if (ev->ev_flags & EVLIST_TIMEOUT) {
 80         /* NOTE: We never need to notify the main thread because of a
 81          * deleted timeout event: all that could happen if we don‘t is
 82          * that the dispatch loop might wake up too early.  But the
 83          * point of notifying the main thread _is_ to wake up the
 84          * dispatch loop early anyway, so we wouldn‘t gain anything by
 85          * doing it.
 86          */
 87         event_queue_remove_timeout(base, ev);
 88     }
 89
 90     if (ev->ev_flags & EVLIST_ACTIVE)
 91         event_queue_remove_active(base, event_to_event_callback(ev));
 92     else if (ev->ev_flags & EVLIST_ACTIVE_LATER)
 93         event_queue_remove_active_later(base, event_to_event_callback(ev));
 94
 95     if (ev->ev_flags & EVLIST_INSERTED) {
 96         event_queue_remove_inserted(base, ev);
 97         if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
 98             res = evmap_io_del_(base, ev->ev_fd, ev);
 99         else
100             res = evmap_signal_del_(base, (int)ev->ev_fd, ev);
101         if (res == 1) {
102             /* evmap says we need to notify the main thread. */
103             notify = 1;
104             res = 0;
105         }
106     }
107
108     /* if we are not in the right thread, we need to wake up the loop */
109     if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
110         evthread_notify_base(base);
111
112     event_debug_note_del_(ev);
113
114     return (res);
115 }

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

结论:

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

时间: 2024-10-14 02:12:56

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

[libevent源码分析] event_add

event_add 把event往当前event中的ev_base追加,如果需要定时,那么tv不能为空 int event_add(struct event *ev, const struct timeval *tv) { struct event_base *base = ev->ev_base; //event_add 会把event加入到他的ev_base成员里 const struct eventop *evsel = base->evsel; //对应linux的epoll相关函数

Libevent源码分析—event_add()

接下来就是将已经初始化的event注册到libevent的事件链表上,通过event_add()来实现,源码位于event.c中. event_add() 这个函数主要完成了下面几件事: 1.将event注册到event_base的I/O多路复用要监听的事件中 2.将event注册到event_base的已注册事件链表中 3.如果传入了超时时间,则删除旧的超时时间,重新设置,并将event添加到event_base的小根堆中: 如果没有传入超时时间,则不会添加到小根堆中. 只有步骤1成功,才会执

【转】libevent源码分析

libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络库.Libevent有几个显著的亮点: (1)事件驱动(event-driven),高性能:(2)轻量级,专注于网络,不如 ACE 那么臃肿庞

Libevent源码分析-timer和signal处理

timer处理 Signal处理 timerfd和signalfd timerfd signalfd timer处理 在Libevent源码分析-event处理流程中,使用了定时器,来看一下源码: evtimer_set(&ev, time_cb, NULL);//设置定时器事件 其中evtimer_set是个宏定义 #define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) //event_set原型 void ev

Libevent源码分析 (1) hello-world

Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libevent的源码,凡是我觉得有必要的内容均一一记录,与君共勉. 首先要说说什么是libevent: libevent是一个事件通知库,libevent API提供一种机制使得我们可以在一个文件描述符(file descriptor)发生特定事件时或者timeout发生时执行指定的回调函数.libevent意

libevent源码分析-event

event结构 event相关接口 Libevent对event的管理 event结构 event是Reactor模式中的最重要的组件.它包含了了一个句柄fd,并设置监听这个句柄上的哪些事件(读/写等),设置了对应的函数指针,在事件到来时,回调函数指针来处理事件. 先看一下event的结构.它位于include/event2/event_struct.h中 struct event { TAILQ_ENTRY(event) ev_active_next; TAILQ_ENTRY(event) e

[libevent源码分析] event_init

libevent采用的是经典的reactor网络框架,集成了信号.定时.网络事件于一体 首先对event_init进行源码剖析 event_init 主要创建event_base对象, struct event_base { const struct eventop *evsel; //lievent支持select epoll kequeue..等网络api,包括init.add.del.dispatch的接口,每种网络框架都支持 void *evbase; //支持相应网络api的 结构对象

[libevent源码分析] event_set

libevent使用event来封装网络事件回调,参数.fd...等一些信息,函数很简单 void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg) { /* Take the current base - caller needs to set the real base later */ ev->ev_base = current_base; /

[libevent源码分析] event_base_dispatch

分析下事件循环 event_base_dispatch int event_base_dispatch(struct event_base *event_base) { return (event_base_loop(event_base, 0)); } int event_base_loop(struct event_base *base, int flags) { const struct eventop *evsel = base->evsel; void *evbase = base->