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