|
1 /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ |
|
2 |
|
3 /* |
|
4 * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> |
|
5 * Copyright 2007-2012 Niels Provos and Nick Mathewson |
|
6 * |
|
7 * Redistribution and use in source and binary forms, with or without |
|
8 * modification, are permitted provided that the following conditions |
|
9 * are met: |
|
10 * 1. Redistributions of source code must retain the above copyright |
|
11 * notice, this list of conditions and the following disclaimer. |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * 3. The name of the author may not be used to endorse or promote products |
|
16 * derived from this software without specific prior written permission. |
|
17 * |
|
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 */ |
|
29 #include "event2/event-config.h" |
|
30 |
|
31 #include <sys/types.h> |
|
32 #ifdef _EVENT_HAVE_SYS_TIME_H |
|
33 #include <sys/time.h> |
|
34 #endif |
|
35 #include <sys/queue.h> |
|
36 #include <poll.h> |
|
37 #include <signal.h> |
|
38 #include <limits.h> |
|
39 #include <stdio.h> |
|
40 #include <stdlib.h> |
|
41 #include <string.h> |
|
42 #include <unistd.h> |
|
43 #include <errno.h> |
|
44 |
|
45 #include "event-internal.h" |
|
46 #include "evsignal-internal.h" |
|
47 #include "log-internal.h" |
|
48 #include "evmap-internal.h" |
|
49 #include "event2/thread.h" |
|
50 #include "evthread-internal.h" |
|
51 |
|
52 struct pollidx { |
|
53 int idxplus1; |
|
54 }; |
|
55 |
|
56 struct pollop { |
|
57 int event_count; /* Highest number alloc */ |
|
58 int nfds; /* Highest number used */ |
|
59 int realloc_copy; /* True iff we must realloc |
|
60 * event_set_copy */ |
|
61 struct pollfd *event_set; |
|
62 struct pollfd *event_set_copy; |
|
63 }; |
|
64 |
|
65 static void *poll_init(struct event_base *); |
|
66 static int poll_add(struct event_base *, int, short old, short events, void *_idx); |
|
67 static int poll_del(struct event_base *, int, short old, short events, void *_idx); |
|
68 static int poll_dispatch(struct event_base *, struct timeval *); |
|
69 static void poll_dealloc(struct event_base *); |
|
70 |
|
71 const struct eventop pollops = { |
|
72 "poll", |
|
73 poll_init, |
|
74 poll_add, |
|
75 poll_del, |
|
76 poll_dispatch, |
|
77 poll_dealloc, |
|
78 0, /* doesn't need_reinit */ |
|
79 EV_FEATURE_FDS, |
|
80 sizeof(struct pollidx), |
|
81 }; |
|
82 |
|
83 static void * |
|
84 poll_init(struct event_base *base) |
|
85 { |
|
86 struct pollop *pollop; |
|
87 |
|
88 if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) |
|
89 return (NULL); |
|
90 |
|
91 evsig_init(base); |
|
92 |
|
93 return (pollop); |
|
94 } |
|
95 |
|
96 #ifdef CHECK_INVARIANTS |
|
97 static void |
|
98 poll_check_ok(struct pollop *pop) |
|
99 { |
|
100 int i, idx; |
|
101 struct event *ev; |
|
102 |
|
103 for (i = 0; i < pop->fd_count; ++i) { |
|
104 idx = pop->idxplus1_by_fd[i]-1; |
|
105 if (idx < 0) |
|
106 continue; |
|
107 EVUTIL_ASSERT(pop->event_set[idx].fd == i); |
|
108 } |
|
109 for (i = 0; i < pop->nfds; ++i) { |
|
110 struct pollfd *pfd = &pop->event_set[i]; |
|
111 EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); |
|
112 } |
|
113 } |
|
114 #else |
|
115 #define poll_check_ok(pop) |
|
116 #endif |
|
117 |
|
118 static int |
|
119 poll_dispatch(struct event_base *base, struct timeval *tv) |
|
120 { |
|
121 int res, i, j, nfds; |
|
122 long msec = -1; |
|
123 struct pollop *pop = base->evbase; |
|
124 struct pollfd *event_set; |
|
125 |
|
126 poll_check_ok(pop); |
|
127 |
|
128 nfds = pop->nfds; |
|
129 |
|
130 #ifndef _EVENT_DISABLE_THREAD_SUPPORT |
|
131 if (base->th_base_lock) { |
|
132 /* If we're using this backend in a multithreaded setting, |
|
133 * then we need to work on a copy of event_set, so that we can |
|
134 * let other threads modify the main event_set while we're |
|
135 * polling. If we're not multithreaded, then we'll skip the |
|
136 * copy step here to save memory and time. */ |
|
137 if (pop->realloc_copy) { |
|
138 struct pollfd *tmp = mm_realloc(pop->event_set_copy, |
|
139 pop->event_count * sizeof(struct pollfd)); |
|
140 if (tmp == NULL) { |
|
141 event_warn("realloc"); |
|
142 return -1; |
|
143 } |
|
144 pop->event_set_copy = tmp; |
|
145 pop->realloc_copy = 0; |
|
146 } |
|
147 memcpy(pop->event_set_copy, pop->event_set, |
|
148 sizeof(struct pollfd)*nfds); |
|
149 event_set = pop->event_set_copy; |
|
150 } else { |
|
151 event_set = pop->event_set; |
|
152 } |
|
153 #else |
|
154 event_set = pop->event_set; |
|
155 #endif |
|
156 |
|
157 if (tv != NULL) { |
|
158 msec = evutil_tv_to_msec(tv); |
|
159 if (msec < 0 || msec > INT_MAX) |
|
160 msec = INT_MAX; |
|
161 } |
|
162 |
|
163 EVBASE_RELEASE_LOCK(base, th_base_lock); |
|
164 |
|
165 res = poll(event_set, nfds, msec); |
|
166 |
|
167 EVBASE_ACQUIRE_LOCK(base, th_base_lock); |
|
168 |
|
169 if (res == -1) { |
|
170 if (errno != EINTR) { |
|
171 event_warn("poll"); |
|
172 return (-1); |
|
173 } |
|
174 |
|
175 return (0); |
|
176 } |
|
177 |
|
178 event_debug(("%s: poll reports %d", __func__, res)); |
|
179 |
|
180 if (res == 0 || nfds == 0) |
|
181 return (0); |
|
182 |
|
183 i = random() % nfds; |
|
184 for (j = 0; j < nfds; j++) { |
|
185 int what; |
|
186 if (++i == nfds) |
|
187 i = 0; |
|
188 what = event_set[i].revents; |
|
189 if (!what) |
|
190 continue; |
|
191 |
|
192 res = 0; |
|
193 |
|
194 /* If the file gets closed notify */ |
|
195 if (what & (POLLHUP|POLLERR)) |
|
196 what |= POLLIN|POLLOUT; |
|
197 if (what & POLLIN) |
|
198 res |= EV_READ; |
|
199 if (what & POLLOUT) |
|
200 res |= EV_WRITE; |
|
201 if (res == 0) |
|
202 continue; |
|
203 |
|
204 evmap_io_active(base, event_set[i].fd, res); |
|
205 } |
|
206 |
|
207 return (0); |
|
208 } |
|
209 |
|
210 static int |
|
211 poll_add(struct event_base *base, int fd, short old, short events, void *_idx) |
|
212 { |
|
213 struct pollop *pop = base->evbase; |
|
214 struct pollfd *pfd = NULL; |
|
215 struct pollidx *idx = _idx; |
|
216 int i; |
|
217 |
|
218 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); |
|
219 if (!(events & (EV_READ|EV_WRITE))) |
|
220 return (0); |
|
221 |
|
222 poll_check_ok(pop); |
|
223 if (pop->nfds + 1 >= pop->event_count) { |
|
224 struct pollfd *tmp_event_set; |
|
225 int tmp_event_count; |
|
226 |
|
227 if (pop->event_count < 32) |
|
228 tmp_event_count = 32; |
|
229 else |
|
230 tmp_event_count = pop->event_count * 2; |
|
231 |
|
232 /* We need more file descriptors */ |
|
233 tmp_event_set = mm_realloc(pop->event_set, |
|
234 tmp_event_count * sizeof(struct pollfd)); |
|
235 if (tmp_event_set == NULL) { |
|
236 event_warn("realloc"); |
|
237 return (-1); |
|
238 } |
|
239 pop->event_set = tmp_event_set; |
|
240 |
|
241 pop->event_count = tmp_event_count; |
|
242 pop->realloc_copy = 1; |
|
243 } |
|
244 |
|
245 i = idx->idxplus1 - 1; |
|
246 |
|
247 if (i >= 0) { |
|
248 pfd = &pop->event_set[i]; |
|
249 } else { |
|
250 i = pop->nfds++; |
|
251 pfd = &pop->event_set[i]; |
|
252 pfd->events = 0; |
|
253 pfd->fd = fd; |
|
254 idx->idxplus1 = i + 1; |
|
255 } |
|
256 |
|
257 pfd->revents = 0; |
|
258 if (events & EV_WRITE) |
|
259 pfd->events |= POLLOUT; |
|
260 if (events & EV_READ) |
|
261 pfd->events |= POLLIN; |
|
262 poll_check_ok(pop); |
|
263 |
|
264 return (0); |
|
265 } |
|
266 |
|
267 /* |
|
268 * Nothing to be done here. |
|
269 */ |
|
270 |
|
271 static int |
|
272 poll_del(struct event_base *base, int fd, short old, short events, void *_idx) |
|
273 { |
|
274 struct pollop *pop = base->evbase; |
|
275 struct pollfd *pfd = NULL; |
|
276 struct pollidx *idx = _idx; |
|
277 int i; |
|
278 |
|
279 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); |
|
280 if (!(events & (EV_READ|EV_WRITE))) |
|
281 return (0); |
|
282 |
|
283 poll_check_ok(pop); |
|
284 i = idx->idxplus1 - 1; |
|
285 if (i < 0) |
|
286 return (-1); |
|
287 |
|
288 /* Do we still want to read or write? */ |
|
289 pfd = &pop->event_set[i]; |
|
290 if (events & EV_READ) |
|
291 pfd->events &= ~POLLIN; |
|
292 if (events & EV_WRITE) |
|
293 pfd->events &= ~POLLOUT; |
|
294 poll_check_ok(pop); |
|
295 if (pfd->events) |
|
296 /* Another event cares about that fd. */ |
|
297 return (0); |
|
298 |
|
299 /* Okay, so we aren't interested in that fd anymore. */ |
|
300 idx->idxplus1 = 0; |
|
301 |
|
302 --pop->nfds; |
|
303 if (i != pop->nfds) { |
|
304 /* |
|
305 * Shift the last pollfd down into the now-unoccupied |
|
306 * position. |
|
307 */ |
|
308 memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], |
|
309 sizeof(struct pollfd)); |
|
310 idx = evmap_io_get_fdinfo(&base->io, pop->event_set[i].fd); |
|
311 EVUTIL_ASSERT(idx); |
|
312 EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); |
|
313 idx->idxplus1 = i + 1; |
|
314 } |
|
315 |
|
316 poll_check_ok(pop); |
|
317 return (0); |
|
318 } |
|
319 |
|
320 static void |
|
321 poll_dealloc(struct event_base *base) |
|
322 { |
|
323 struct pollop *pop = base->evbase; |
|
324 |
|
325 evsig_dealloc(base); |
|
326 if (pop->event_set) |
|
327 mm_free(pop->event_set); |
|
328 if (pop->event_set_copy) |
|
329 mm_free(pop->event_set_copy); |
|
330 |
|
331 memset(pop, 0, sizeof(struct pollop)); |
|
332 mm_free(pop); |
|
333 } |