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的设计之后再来分析。
来源:https://www.cnblogs.com/lit10050528/p/6330925.html