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的设计之后再来分析。

  • 相关阅读:
    TCP与UDP的差别以及TCP三次握手、四次挥手
    MAC帧格式、IPV4数据报格式、TCP报文格式、UDP数据报格式
    维特比算法(Viterbi)-实例讲解(暴力破解+代码实现)
    对ajax的理解
    get与post两种方式的优缺点
    什么是Ajax和JSON,它们的优缺点
    浅谈一下如何避免用户多次点击造成的多次请求
    ajax是什么?
    同步和异步的区别?
    如何解决跨域问题
  • 原文地址:https://www.cnblogs.com/lit10050528/p/6330925.html
Copyright © 2011-2022 走看看