Line | Count | Source (jump to first uncovered line) |
1 | | /* $OpenBSD: select.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 | | #include "evconfig-private.h" |
31 | | |
32 | | #ifdef EVENT__HAVE_SELECT |
33 | | |
34 | | #ifdef __APPLE__ |
35 | | /* Apple wants us to define this if we might ever pass more than |
36 | | * FD_SETSIZE bits to select(). */ |
37 | | #define _DARWIN_UNLIMITED_SELECT |
38 | | #endif |
39 | | |
40 | | #include <sys/types.h> |
41 | | #ifdef EVENT__HAVE_SYS_TIME_H |
42 | | #include <sys/time.h> |
43 | | #endif |
44 | | #ifdef EVENT__HAVE_SYS_SELECT_H |
45 | | #include <sys/select.h> |
46 | | #endif |
47 | | #include <sys/queue.h> |
48 | | #include <signal.h> |
49 | | #include <stdio.h> |
50 | | #include <stdlib.h> |
51 | | #include <string.h> |
52 | | #include <unistd.h> |
53 | | #include <errno.h> |
54 | | |
55 | | #include "event-internal.h" |
56 | | #include "evsignal-internal.h" |
57 | | #include "event2/thread.h" |
58 | | #include "evthread-internal.h" |
59 | | #include "log-internal.h" |
60 | | #include "evmap-internal.h" |
61 | | |
62 | | #ifndef EVENT__HAVE_FD_MASK |
63 | | /* This type is mandatory, but Android doesn't define it. */ |
64 | | typedef unsigned long fd_mask; |
65 | | #endif |
66 | | |
67 | | #ifndef NFDBITS |
68 | | #define NFDBITS (sizeof(fd_mask)*8) |
69 | | #endif |
70 | | |
71 | | /* Divide positive x by y, rounding up. */ |
72 | 0 | #define DIV_ROUNDUP(x, y) (((x)+((y)-1))/(y)) |
73 | | |
74 | | /* How many bytes to allocate for N fds? */ |
75 | | #define SELECT_ALLOC_SIZE(n) \ |
76 | 0 | (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask)) |
77 | | |
78 | | struct selectop { |
79 | | int event_fds; /* Highest fd in fd set */ |
80 | | int event_fdsz; |
81 | | int resize_out_sets; |
82 | | fd_set *event_readset_in; |
83 | | fd_set *event_writeset_in; |
84 | | fd_set *event_readset_out; |
85 | | fd_set *event_writeset_out; |
86 | | }; |
87 | | |
88 | | static void *select_init(struct event_base *); |
89 | | static int select_add(struct event_base *, int, short old, short events, void*); |
90 | | static int select_del(struct event_base *, int, short old, short events, void*); |
91 | | static int select_dispatch(struct event_base *, struct timeval *); |
92 | | static void select_dealloc(struct event_base *); |
93 | | |
94 | | const struct eventop selectops = { |
95 | | "select", |
96 | | select_init, |
97 | | select_add, |
98 | | select_del, |
99 | | select_dispatch, |
100 | | select_dealloc, |
101 | | 0, /* doesn't need reinit. */ |
102 | | EV_FEATURE_FDS, |
103 | | 0, |
104 | | }; |
105 | | |
106 | | static int select_resize(struct selectop *sop, int fdsz); |
107 | | static void select_free_selectop(struct selectop *sop); |
108 | | |
109 | | static void * |
110 | | select_init(struct event_base *base) |
111 | 0 | { |
112 | 0 | struct selectop *sop; |
113 | 0 |
|
114 | 0 | if (!(sop = mm_calloc(1, sizeof(struct selectop)))) |
115 | 0 | return (NULL); |
116 | 0 | |
117 | 0 | if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) { |
118 | 0 | select_free_selectop(sop); |
119 | 0 | return (NULL); |
120 | 0 | } |
121 | 0 | |
122 | 0 | evsig_init_(base); |
123 | 0 |
|
124 | 0 | evutil_weakrand_seed_(&base->weakrand_seed, 0); |
125 | 0 |
|
126 | 0 | return (sop); |
127 | 0 | } |
128 | | |
129 | | #ifdef CHECK_INVARIANTS |
130 | | static void |
131 | | check_selectop(struct selectop *sop) |
132 | | { |
133 | | /* nothing to be done here */ |
134 | | } |
135 | | #else |
136 | 0 | #define check_selectop(sop) do { (void) sop; } while (0) |
137 | | #endif |
138 | | |
139 | | static int |
140 | | select_dispatch(struct event_base *base, struct timeval *tv) |
141 | 0 | { |
142 | 0 | int res=0, i, j, nfds; |
143 | 0 | struct selectop *sop = base->evbase; |
144 | 0 |
|
145 | 0 | check_selectop(sop); |
146 | 0 | if (sop->resize_out_sets) { |
147 | 0 | fd_set *readset_out=NULL, *writeset_out=NULL; |
148 | 0 | size_t sz = sop->event_fdsz; |
149 | 0 | if (!(readset_out = mm_realloc(sop->event_readset_out, sz))) |
150 | 0 | return (-1); |
151 | 0 | sop->event_readset_out = readset_out; |
152 | 0 | if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) { |
153 | 0 | /* We don't free readset_out here, since it was |
154 | 0 | * already successfully reallocated. The next time |
155 | 0 | * we call select_dispatch, the realloc will be a |
156 | 0 | * no-op. */ |
157 | 0 | return (-1); |
158 | 0 | } |
159 | 0 | sop->event_writeset_out = writeset_out; |
160 | 0 | sop->resize_out_sets = 0; |
161 | 0 | } |
162 | 0 |
|
163 | 0 | memcpy(sop->event_readset_out, sop->event_readset_in, |
164 | 0 | sop->event_fdsz); |
165 | 0 | memcpy(sop->event_writeset_out, sop->event_writeset_in, |
166 | 0 | sop->event_fdsz); |
167 | 0 |
|
168 | 0 | nfds = sop->event_fds+1; |
169 | 0 |
|
170 | 0 | EVBASE_RELEASE_LOCK(base, th_base_lock); |
171 | 0 |
|
172 | 0 | res = select(nfds, sop->event_readset_out, |
173 | 0 | sop->event_writeset_out, NULL, tv); |
174 | 0 |
|
175 | 0 | EVBASE_ACQUIRE_LOCK(base, th_base_lock); |
176 | 0 |
|
177 | 0 | check_selectop(sop); |
178 | 0 |
|
179 | 0 | if (res == -1) { |
180 | 0 | if (errno != EINTR) { |
181 | 0 | event_warn("select"); |
182 | 0 | return (-1); |
183 | 0 | } |
184 | 0 | |
185 | 0 | return (0); |
186 | 0 | } |
187 | 0 | |
188 | 0 | event_debug(("%s: select reports %d", __func__, res)); |
189 | 0 |
|
190 | 0 | check_selectop(sop); |
191 | 0 | i = evutil_weakrand_range_(&base->weakrand_seed, nfds); |
192 | 0 | for (j = 0; j < nfds; ++j) { |
193 | 0 | if (++i >= nfds) |
194 | 0 | i = 0; |
195 | 0 | res = 0; |
196 | 0 | if (FD_ISSET(i, sop->event_readset_out)) |
197 | 0 | res |= EV_READ; |
198 | 0 | if (FD_ISSET(i, sop->event_writeset_out)) |
199 | 0 | res |= EV_WRITE; |
200 | 0 |
|
201 | 0 | if (res == 0) |
202 | 0 | continue; |
203 | 0 | |
204 | 0 | evmap_io_active_(base, i, res); |
205 | 0 | } |
206 | 0 | check_selectop(sop); |
207 | 0 |
|
208 | 0 | return (0); |
209 | 0 | } |
210 | | |
211 | | static int |
212 | | select_resize(struct selectop *sop, int fdsz) |
213 | 0 | { |
214 | 0 | fd_set *readset_in = NULL; |
215 | 0 | fd_set *writeset_in = NULL; |
216 | 0 |
|
217 | 0 | if (sop->event_readset_in) |
218 | 0 | check_selectop(sop); |
219 | 0 |
|
220 | 0 | if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL) |
221 | 0 | goto error; |
222 | 0 | sop->event_readset_in = readset_in; |
223 | 0 | if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) { |
224 | 0 | /* Note that this will leave event_readset_in expanded. |
225 | 0 | * That's okay; we wouldn't want to free it, since that would |
226 | 0 | * change the semantics of select_resize from "expand the |
227 | 0 | * readset_in and writeset_in, or return -1" to "expand the |
228 | 0 | * *set_in members, or trash them and return -1." |
229 | 0 | */ |
230 | 0 | goto error; |
231 | 0 | } |
232 | 0 | sop->event_writeset_in = writeset_in; |
233 | 0 | sop->resize_out_sets = 1; |
234 | 0 |
|
235 | 0 | memset((char *)sop->event_readset_in + sop->event_fdsz, 0, |
236 | 0 | fdsz - sop->event_fdsz); |
237 | 0 | memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, |
238 | 0 | fdsz - sop->event_fdsz); |
239 | 0 |
|
240 | 0 | sop->event_fdsz = fdsz; |
241 | 0 | check_selectop(sop); |
242 | 0 |
|
243 | 0 | return (0); |
244 | 0 |
|
245 | 0 | error: |
246 | 0 | event_warn("malloc"); |
247 | 0 | return (-1); |
248 | 0 | } |
249 | | |
250 | | |
251 | | static int |
252 | | select_add(struct event_base *base, int fd, short old, short events, void *p) |
253 | 0 | { |
254 | 0 | struct selectop *sop = base->evbase; |
255 | 0 | (void) p; |
256 | 0 |
|
257 | 0 | EVUTIL_ASSERT((events & EV_SIGNAL) == 0); |
258 | 0 | check_selectop(sop); |
259 | 0 | /* |
260 | 0 | * Keep track of the highest fd, so that we can calculate the size |
261 | 0 | * of the fd_sets for select(2) |
262 | 0 | */ |
263 | 0 | if (sop->event_fds < fd) { |
264 | 0 | int fdsz = sop->event_fdsz; |
265 | 0 |
|
266 | 0 | if (fdsz < (int)sizeof(fd_mask)) |
267 | 0 | fdsz = (int)sizeof(fd_mask); |
268 | 0 |
|
269 | 0 | /* In theory we should worry about overflow here. In |
270 | 0 | * reality, though, the highest fd on a unixy system will |
271 | 0 | * not overflow here. XXXX */ |
272 | 0 | while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1)) |
273 | 0 | fdsz *= 2; |
274 | 0 |
|
275 | 0 | if (fdsz != sop->event_fdsz) { |
276 | 0 | if (select_resize(sop, fdsz)) { |
277 | 0 | check_selectop(sop); |
278 | 0 | return (-1); |
279 | 0 | } |
280 | 0 | } |
281 | 0 |
|
282 | 0 | sop->event_fds = fd; |
283 | 0 | } |
284 | 0 |
|
285 | 0 | if (events & EV_READ) |
286 | 0 | FD_SET(fd, sop->event_readset_in); |
287 | 0 | if (events & EV_WRITE) |
288 | 0 | FD_SET(fd, sop->event_writeset_in); |
289 | 0 | check_selectop(sop); |
290 | 0 |
|
291 | 0 | return (0); |
292 | 0 | } |
293 | | |
294 | | /* |
295 | | * Nothing to be done here. |
296 | | */ |
297 | | |
298 | | static int |
299 | | select_del(struct event_base *base, int fd, short old, short events, void *p) |
300 | 0 | { |
301 | 0 | struct selectop *sop = base->evbase; |
302 | 0 | (void)p; |
303 | 0 |
|
304 | 0 | EVUTIL_ASSERT((events & EV_SIGNAL) == 0); |
305 | 0 | check_selectop(sop); |
306 | 0 |
|
307 | 0 | if (sop->event_fds < fd) { |
308 | 0 | check_selectop(sop); |
309 | 0 | return (0); |
310 | 0 | } |
311 | 0 |
|
312 | 0 | if (events & EV_READ) |
313 | 0 | FD_CLR(fd, sop->event_readset_in); |
314 | 0 |
|
315 | 0 | if (events & EV_WRITE) |
316 | 0 | FD_CLR(fd, sop->event_writeset_in); |
317 | 0 |
|
318 | 0 | check_selectop(sop); |
319 | 0 | return (0); |
320 | 0 | } |
321 | | |
322 | | static void |
323 | | select_free_selectop(struct selectop *sop) |
324 | 0 | { |
325 | 0 | if (sop->event_readset_in) |
326 | 0 | mm_free(sop->event_readset_in); |
327 | 0 | if (sop->event_writeset_in) |
328 | 0 | mm_free(sop->event_writeset_in); |
329 | 0 | if (sop->event_readset_out) |
330 | 0 | mm_free(sop->event_readset_out); |
331 | 0 | if (sop->event_writeset_out) |
332 | 0 | mm_free(sop->event_writeset_out); |
333 | 0 |
|
334 | 0 | memset(sop, 0, sizeof(struct selectop)); |
335 | 0 | mm_free(sop); |
336 | 0 | } |
337 | | |
338 | | static void |
339 | | select_dealloc(struct event_base *base) |
340 | 0 | { |
341 | 0 | evsig_dealloc_(base); |
342 | 0 |
|
343 | 0 | select_free_selectop(base->evbase); |
344 | 0 | } |
345 | | |
346 | | #endif /* EVENT__HAVE_SELECT */ |