首页 > 代码库 > libevent源码分析:event_add、event_del

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 #endif101 102         /*103          * for persistent timeout events, we remember the104          * 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_TIMEOUT112         if (ev->ev_flags & EVLIST_TIMEOUT) {113             event_queue_remove_timeout(base, ev);114         }115 #endif116 117         /* Check if it is active due to a timeout.  Rescheduling118          * this timeout before the callback can be executed119          * 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 executing124                  * this event in a loop125                  */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_TIMEOUT139         was_common = is_common_timeout(&ev->ev_timeout, base);140         old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);141 #endif142 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_TIMEOUT160         event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);161 #else162         event_queue_insert_timeout(base, ev);163 #endif164 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 it174              * was before: if so, we will need to tell the main175              * thread to wake up earlier than it would otherwise.176              * We double check the timeout of the top element to177              * 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         else100             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的设计之后再来分析。

libevent源码分析:event_add、event_del