select.c revision 1.1.1.1 1 /* $NetBSD: select.c,v 1.1.1.1 2013/12/27 23:31:18 christos Exp $ */
2
3 /* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
4
5 /*
6 * Copyright 2000-2007 Niels Provos <provos (at) citi.umich.edu>
7 * Copyright 2007-2012 Niels Provos and Nick Mathewson
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include "event2/event-config.h"
32 #include "evconfig-private.h"
33
34 #ifdef EVENT__HAVE_SELECT
35
36 #ifdef __APPLE__
37 /* Apple wants us to define this if we might ever pass more than
38 * FD_SETSIZE bits to select(). */
39 #define _DARWIN_UNLIMITED_SELECT
40 #endif
41
42 #include <sys/types.h>
43 #ifdef EVENT__HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #ifdef EVENT__HAVE_SYS_SELECT_H
47 #include <sys/select.h>
48 #endif
49 #include <sys/queue.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <errno.h>
56
57 #include "event-internal.h"
58 #include "evsignal-internal.h"
59 #include "event2/thread.h"
60 #include "evthread-internal.h"
61 #include "log-internal.h"
62 #include "evmap-internal.h"
63
64 #ifndef EVENT__HAVE_FD_MASK
65 /* This type is mandatory, but Android doesn't define it. */
66 typedef unsigned long fd_mask;
67 #endif
68
69 #ifndef NFDBITS
70 #define NFDBITS (sizeof(fd_mask)*8)
71 #endif
72
73 /* Divide positive x by y, rounding up. */
74 #define DIV_ROUNDUP(x, y) (((x)+((y)-1))/(y))
75
76 /* How many bytes to allocate for N fds? */
77 #define SELECT_ALLOC_SIZE(n) \
78 (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
79
80 struct selectop {
81 int event_fds; /* Highest fd in fd set */
82 int event_fdsz;
83 int resize_out_sets;
84 fd_set *event_readset_in;
85 fd_set *event_writeset_in;
86 fd_set *event_readset_out;
87 fd_set *event_writeset_out;
88 };
89
90 static void *select_init(struct event_base *);
91 static int select_add(struct event_base *, int, short old, short events, void*);
92 static int select_del(struct event_base *, int, short old, short events, void*);
93 static int select_dispatch(struct event_base *, struct timeval *);
94 static void select_dealloc(struct event_base *);
95
96 const struct eventop selectops = {
97 "select",
98 select_init,
99 select_add,
100 select_del,
101 select_dispatch,
102 select_dealloc,
103 0, /* doesn't need reinit. */
104 EV_FEATURE_FDS,
105 0,
106 };
107
108 static int select_resize(struct selectop *sop, int fdsz);
109 static void select_free_selectop(struct selectop *sop);
110
111 static void *
112 select_init(struct event_base *base)
113 {
114 struct selectop *sop;
115
116 if (!(sop = mm_calloc(1, sizeof(struct selectop))))
117 return (NULL);
118
119 if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
120 select_free_selectop(sop);
121 return (NULL);
122 }
123
124 evsig_init_(base);
125
126 evutil_weakrand_seed_(&base->weakrand_seed, 0);
127
128 return (sop);
129 }
130
131 #ifdef CHECK_INVARIANTS
132 static void
133 check_selectop(struct selectop *sop)
134 {
135 /* nothing to be done here */
136 }
137 #else
138 #define check_selectop(sop) do { (void) sop; } while (0)
139 #endif
140
141 static int
142 select_dispatch(struct event_base *base, struct timeval *tv)
143 {
144 int res=0, i, j, nfds;
145 struct selectop *sop = base->evbase;
146
147 check_selectop(sop);
148 if (sop->resize_out_sets) {
149 fd_set *readset_out=NULL, *writeset_out=NULL;
150 size_t sz = sop->event_fdsz;
151 if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
152 return (-1);
153 sop->event_readset_out = readset_out;
154 if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
155 /* We don't free readset_out here, since it was
156 * already successfully reallocated. The next time
157 * we call select_dispatch, the realloc will be a
158 * no-op. */
159 return (-1);
160 }
161 sop->event_writeset_out = writeset_out;
162 sop->resize_out_sets = 0;
163 }
164
165 memcpy(sop->event_readset_out, sop->event_readset_in,
166 sop->event_fdsz);
167 memcpy(sop->event_writeset_out, sop->event_writeset_in,
168 sop->event_fdsz);
169
170 nfds = sop->event_fds+1;
171
172 EVBASE_RELEASE_LOCK(base, th_base_lock);
173
174 res = select(nfds, sop->event_readset_out,
175 sop->event_writeset_out, NULL, tv);
176
177 EVBASE_ACQUIRE_LOCK(base, th_base_lock);
178
179 check_selectop(sop);
180
181 if (res == -1) {
182 if (errno != EINTR) {
183 event_warn("select");
184 return (-1);
185 }
186
187 return (0);
188 }
189
190 event_debug(("%s: select reports %d", __func__, res));
191
192 check_selectop(sop);
193 i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
194 for (j = 0; j < nfds; ++j) {
195 if (++i >= nfds)
196 i = 0;
197 res = 0;
198 if (FD_ISSET(i, sop->event_readset_out))
199 res |= EV_READ;
200 if (FD_ISSET(i, sop->event_writeset_out))
201 res |= EV_WRITE;
202
203 if (res == 0)
204 continue;
205
206 evmap_io_active_(base, i, res);
207 }
208 check_selectop(sop);
209
210 return (0);
211 }
212
213 static int
214 select_resize(struct selectop *sop, int fdsz)
215 {
216 fd_set *readset_in = NULL;
217 fd_set *writeset_in = NULL;
218
219 if (sop->event_readset_in)
220 check_selectop(sop);
221
222 if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
223 goto error;
224 sop->event_readset_in = readset_in;
225 if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
226 /* Note that this will leave event_readset_in expanded.
227 * That's okay; we wouldn't want to free it, since that would
228 * change the semantics of select_resize from "expand the
229 * readset_in and writeset_in, or return -1" to "expand the
230 * *set_in members, or trash them and return -1."
231 */
232 goto error;
233 }
234 sop->event_writeset_in = writeset_in;
235 sop->resize_out_sets = 1;
236
237 memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
238 fdsz - sop->event_fdsz);
239 memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
240 fdsz - sop->event_fdsz);
241
242 sop->event_fdsz = fdsz;
243 check_selectop(sop);
244
245 return (0);
246
247 error:
248 event_warn("malloc");
249 return (-1);
250 }
251
252
253 static int
254 select_add(struct event_base *base, int fd, short old, short events, void *p)
255 {
256 struct selectop *sop = base->evbase;
257 (void) p;
258
259 EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
260 check_selectop(sop);
261 /*
262 * Keep track of the highest fd, so that we can calculate the size
263 * of the fd_sets for select(2)
264 */
265 if (sop->event_fds < fd) {
266 int fdsz = sop->event_fdsz;
267
268 if (fdsz < (int)sizeof(fd_mask))
269 fdsz = (int)sizeof(fd_mask);
270
271 /* In theory we should worry about overflow here. In
272 * reality, though, the highest fd on a unixy system will
273 * not overflow here. XXXX */
274 while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
275 fdsz *= 2;
276
277 if (fdsz != sop->event_fdsz) {
278 if (select_resize(sop, fdsz)) {
279 check_selectop(sop);
280 return (-1);
281 }
282 }
283
284 sop->event_fds = fd;
285 }
286
287 if (events & EV_READ)
288 FD_SET(fd, sop->event_readset_in);
289 if (events & EV_WRITE)
290 FD_SET(fd, sop->event_writeset_in);
291 check_selectop(sop);
292
293 return (0);
294 }
295
296 /*
297 * Nothing to be done here.
298 */
299
300 static int
301 select_del(struct event_base *base, int fd, short old, short events, void *p)
302 {
303 struct selectop *sop = base->evbase;
304 (void)p;
305
306 EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
307 check_selectop(sop);
308
309 if (sop->event_fds < fd) {
310 check_selectop(sop);
311 return (0);
312 }
313
314 if (events & EV_READ)
315 FD_CLR(fd, sop->event_readset_in);
316
317 if (events & EV_WRITE)
318 FD_CLR(fd, sop->event_writeset_in);
319
320 check_selectop(sop);
321 return (0);
322 }
323
324 static void
325 select_free_selectop(struct selectop *sop)
326 {
327 if (sop->event_readset_in)
328 mm_free(sop->event_readset_in);
329 if (sop->event_writeset_in)
330 mm_free(sop->event_writeset_in);
331 if (sop->event_readset_out)
332 mm_free(sop->event_readset_out);
333 if (sop->event_writeset_out)
334 mm_free(sop->event_writeset_out);
335
336 memset(sop, 0, sizeof(struct selectop));
337 mm_free(sop);
338 }
339
340 static void
341 select_dealloc(struct event_base *base)
342 {
343 evsig_dealloc_(base);
344
345 select_free_selectop(base->evbase);
346 }
347
348 #endif /* EVENT__HAVE_SELECT */
349