首页 > 代码库 > 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
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。