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 | | 1, /* 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 | |
|
114 | 0 | if (!(sop = mm_calloc(1, sizeof(struct selectop)))) |
115 | 0 | return (NULL); |
116 | | |
117 | 0 | if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) { |
118 | 0 | select_free_selectop(sop); |
119 | 0 | return (NULL); |
120 | 0 | } |
121 | | |
122 | 0 | if (sigfd_init_(base) < 0) |
123 | 0 | evsig_init_(base); |
124 | |
|
125 | 0 | evutil_weakrand_seed_(&base->weakrand_seed, 0); |
126 | |
|
127 | 0 | return (sop); |
128 | 0 | } |
129 | | |
130 | | #ifdef CHECK_INVARIANTS |
131 | | static void |
132 | | check_selectop(struct selectop *sop) |
133 | | { |
134 | | /* nothing to be done here */ |
135 | | } |
136 | | #else |
137 | 0 | #define check_selectop(sop) do { (void) sop; } while (0) |
138 | | #endif |
139 | | |
140 | | static int |
141 | | select_dispatch(struct event_base *base, struct timeval *tv) |
142 | 0 | { |
143 | 0 | int res=0, i, j, nfds; |
144 | 0 | struct selectop *sop = base->evbase; |
145 | |
|
146 | 0 | check_selectop(sop); |
147 | 0 | if (sop->resize_out_sets) { |
148 | 0 | fd_set *readset_out=NULL, *writeset_out=NULL; |
149 | 0 | size_t sz = sop->event_fdsz; |
150 | 0 | if (!(readset_out = mm_realloc(sop->event_readset_out, sz))) |
151 | 0 | return (-1); |
152 | 0 | sop->event_readset_out = readset_out; |
153 | 0 | if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) { |
154 | | /* We don't free readset_out here, since it was |
155 | | * already successfully reallocated. The next time |
156 | | * we call select_dispatch, the realloc will be a |
157 | | * no-op. */ |
158 | 0 | return (-1); |
159 | 0 | } |
160 | 0 | sop->event_writeset_out = writeset_out; |
161 | 0 | sop->resize_out_sets = 0; |
162 | 0 | } |
163 | | |
164 | 0 | memcpy(sop->event_readset_out, sop->event_readset_in, |
165 | 0 | sop->event_fdsz); |
166 | 0 | memcpy(sop->event_writeset_out, sop->event_writeset_in, |
167 | 0 | sop->event_fdsz); |
168 | |
|
169 | 0 | nfds = sop->event_fds+1; |
170 | |
|
171 | 0 | EVBASE_RELEASE_LOCK(base, th_base_lock); |
172 | |
|
173 | 0 | res = select(nfds, sop->event_readset_out, |
174 | 0 | sop->event_writeset_out, NULL, tv); |
175 | |
|
176 | 0 | EVBASE_ACQUIRE_LOCK(base, th_base_lock); |
177 | |
|
178 | 0 | check_selectop(sop); |
179 | |
|
180 | 0 | if (res == -1) { |
181 | 0 | if (errno != EINTR) { |
182 | 0 | event_warn("select"); |
183 | 0 | return (-1); |
184 | 0 | } |
185 | | |
186 | 0 | return (0); |
187 | 0 | } |
188 | | |
189 | 0 | event_debug(("%s: select reports %d", __func__, res)); |
190 | |
|
191 | 0 | check_selectop(sop); |
192 | 0 | i = evutil_weakrand_range_(&base->weakrand_seed, nfds); |
193 | 0 | for (j = 0; j < nfds; ++j) { |
194 | 0 | if (++i >= nfds) |
195 | 0 | i = 0; |
196 | 0 | res = 0; |
197 | 0 | if (FD_ISSET(i, sop->event_readset_out)) |
198 | 0 | res |= EV_READ; |
199 | 0 | if (FD_ISSET(i, sop->event_writeset_out)) |
200 | 0 | res |= EV_WRITE; |
201 | |
|
202 | 0 | if (res == 0) |
203 | 0 | continue; |
204 | | |
205 | 0 | evmap_io_active_(base, i, res); |
206 | 0 | } |
207 | 0 | check_selectop(sop); |
208 | |
|
209 | 0 | return (0); |
210 | 0 | } |
211 | | |
212 | | static int |
213 | | select_resize(struct selectop *sop, int fdsz) |
214 | 0 | { |
215 | 0 | fd_set *readset_in = NULL; |
216 | 0 | fd_set *writeset_in = NULL; |
217 | |
|
218 | 0 | if (sop->event_readset_in) |
219 | 0 | check_selectop(sop); |
220 | |
|
221 | 0 | if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL) |
222 | 0 | goto error; |
223 | 0 | sop->event_readset_in = readset_in; |
224 | 0 | if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) { |
225 | | /* Note that this will leave event_readset_in expanded. |
226 | | * That's okay; we wouldn't want to free it, since that would |
227 | | * change the semantics of select_resize from "expand the |
228 | | * readset_in and writeset_in, or return -1" to "expand the |
229 | | * *set_in members, or trash them and return -1." |
230 | | */ |
231 | 0 | goto error; |
232 | 0 | } |
233 | 0 | sop->event_writeset_in = writeset_in; |
234 | 0 | sop->resize_out_sets = 1; |
235 | |
|
236 | 0 | memset((char *)sop->event_readset_in + sop->event_fdsz, 0, |
237 | 0 | fdsz - sop->event_fdsz); |
238 | 0 | memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, |
239 | 0 | fdsz - sop->event_fdsz); |
240 | |
|
241 | 0 | sop->event_fdsz = fdsz; |
242 | 0 | check_selectop(sop); |
243 | |
|
244 | 0 | return (0); |
245 | | |
246 | 0 | error: |
247 | 0 | event_warn("malloc"); |
248 | 0 | return (-1); |
249 | 0 | } |
250 | | |
251 | | |
252 | | static int |
253 | | select_add(struct event_base *base, int fd, short old, short events, void *p) |
254 | 0 | { |
255 | 0 | struct selectop *sop = base->evbase; |
256 | 0 | (void) p; |
257 | |
|
258 | 0 | EVUTIL_ASSERT((events & EV_SIGNAL) == 0); |
259 | 0 | check_selectop(sop); |
260 | | /* |
261 | | * Keep track of the highest fd, so that we can calculate the size |
262 | | * of the fd_sets for select(2) |
263 | | */ |
264 | 0 | if (sop->event_fds < fd) { |
265 | 0 | int fdsz = sop->event_fdsz; |
266 | |
|
267 | 0 | if (fdsz < (int)sizeof(fd_mask)) |
268 | 0 | fdsz = (int)sizeof(fd_mask); |
269 | | |
270 | | /* In theory we should worry about overflow here. In |
271 | | * reality, though, the highest fd on a unixy system will |
272 | | * not overflow here. XXXX */ |
273 | 0 | while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1)) |
274 | 0 | fdsz *= 2; |
275 | |
|
276 | 0 | if (fdsz != sop->event_fdsz) { |
277 | 0 | if (select_resize(sop, fdsz)) { |
278 | 0 | check_selectop(sop); |
279 | 0 | return (-1); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | 0 | sop->event_fds = fd; |
284 | 0 | } |
285 | | |
286 | 0 | if (events & EV_READ) |
287 | 0 | FD_SET(fd, sop->event_readset_in); |
288 | 0 | if (events & EV_WRITE) |
289 | 0 | FD_SET(fd, sop->event_writeset_in); |
290 | 0 | check_selectop(sop); |
291 | |
|
292 | 0 | return (0); |
293 | 0 | } |
294 | | |
295 | | /* |
296 | | * Nothing to be done here. |
297 | | */ |
298 | | |
299 | | static int |
300 | | select_del(struct event_base *base, int fd, short old, short events, void *p) |
301 | 0 | { |
302 | 0 | struct selectop *sop = base->evbase; |
303 | 0 | (void)p; |
304 | |
|
305 | 0 | EVUTIL_ASSERT((events & EV_SIGNAL) == 0); |
306 | 0 | check_selectop(sop); |
307 | |
|
308 | 0 | if (sop->event_fds < fd) { |
309 | 0 | check_selectop(sop); |
310 | 0 | return (0); |
311 | 0 | } |
312 | | |
313 | 0 | if (events & EV_READ) |
314 | 0 | FD_CLR(fd, sop->event_readset_in); |
315 | |
|
316 | 0 | if (events & EV_WRITE) |
317 | 0 | FD_CLR(fd, sop->event_writeset_in); |
318 | |
|
319 | 0 | check_selectop(sop); |
320 | 0 | return (0); |
321 | 0 | } |
322 | | |
323 | | static void |
324 | | select_free_selectop(struct selectop *sop) |
325 | 0 | { |
326 | 0 | if (sop->event_readset_in) |
327 | 0 | mm_free(sop->event_readset_in); |
328 | 0 | if (sop->event_writeset_in) |
329 | 0 | mm_free(sop->event_writeset_in); |
330 | 0 | if (sop->event_readset_out) |
331 | 0 | mm_free(sop->event_readset_out); |
332 | 0 | if (sop->event_writeset_out) |
333 | 0 | mm_free(sop->event_writeset_out); |
334 | |
|
335 | 0 | memset(sop, 0, sizeof(struct selectop)); |
336 | 0 | mm_free(sop); |
337 | 0 | } |
338 | | |
339 | | static void |
340 | | select_dealloc(struct event_base *base) |
341 | 0 | { |
342 | 0 | evsig_dealloc_(base); |
343 | |
|
344 | 0 | select_free_selectop(base->evbase); |
345 | 0 | } |
346 | | |
347 | | #endif /* EVENT__HAVE_SELECT */ |