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