zoukankan      html  css  js  c++  java
  • 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的设计之后再来分析。

  • 相关阅读:
    POJ 1681 Painter's Problem(高斯消元法)
    HDU 3530 Subsequence(单调队列)
    HDU 4302 Holedox Eating(优先队列或者线段树)
    POJ 2947 Widget Factory(高斯消元法,解模线性方程组)
    HDU 3635 Dragon Balls(并查集)
    HDU 4301 Divide Chocolate(找规律,DP)
    POJ 1753 Flip Game(高斯消元)
    POJ 3185 The Water Bowls(高斯消元)
    克琳:http://liyu.eu5.org
    WinDbg使用
  • 原文地址:https://www.cnblogs.com/lit10050528/p/6330925.html
Copyright © 2011-2022 走看看