OLD | NEW |
| (Empty) |
1 /* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ | |
2 | |
3 /* | |
4 * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> | |
5 * All rights reserved. | |
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 #ifdef HAVE_CONFIG_H | |
30 #include "config.h" | |
31 #endif | |
32 | |
33 #include <sys/types.h> | |
34 #ifdef HAVE_SYS_TIME_H | |
35 #include <sys/time.h> | |
36 #else | |
37 #include <sys/_libevent_time.h> | |
38 #endif | |
39 #ifdef HAVE_SYS_SELECT_H | |
40 #include <sys/select.h> | |
41 #endif | |
42 #include <sys/queue.h> | |
43 #include <signal.h> | |
44 #include <stdio.h> | |
45 #include <stdlib.h> | |
46 #include <string.h> | |
47 #include <unistd.h> | |
48 #include <errno.h> | |
49 #ifdef CHECK_INVARIANTS | |
50 #include <assert.h> | |
51 #endif | |
52 | |
53 #include "event.h" | |
54 #include "evutil.h" | |
55 #include "event-internal.h" | |
56 #include "evsignal.h" | |
57 #include "log.h" | |
58 | |
59 #ifndef howmany | |
60 #define howmany(x, y) (((x)+((y)-1))/(y)) | |
61 #endif | |
62 | |
63 #ifndef _EVENT_HAVE_FD_MASK | |
64 /* This type is mandatory, but Android doesn't define it. */ | |
65 #undef NFDBITS | |
66 #define NFDBITS (sizeof(long)*8) | |
67 typedef unsigned long fd_mask; | |
68 #endif | |
69 | |
70 struct selectop { | |
71 int event_fds; /* Highest fd in fd set */ | |
72 int event_fdsz; | |
73 fd_set *event_readset_in; | |
74 fd_set *event_writeset_in; | |
75 fd_set *event_readset_out; | |
76 fd_set *event_writeset_out; | |
77 struct event **event_r_by_fd; | |
78 struct event **event_w_by_fd; | |
79 }; | |
80 | |
81 static void *select_init (struct event_base *); | |
82 static int select_add (void *, struct event *); | |
83 static int select_del (void *, struct event *); | |
84 static int select_dispatch (struct event_base *, void *, struct timeval *); | |
85 static void select_dealloc (struct event_base *, void *); | |
86 | |
87 const struct eventop selectops = { | |
88 "select", | |
89 select_init, | |
90 select_add, | |
91 select_del, | |
92 select_dispatch, | |
93 select_dealloc, | |
94 0 | |
95 }; | |
96 | |
97 static int select_resize(struct selectop *sop, int fdsz); | |
98 | |
99 static void * | |
100 select_init(struct event_base *base) | |
101 { | |
102 struct selectop *sop; | |
103 | |
104 /* Disable select when this environment variable is set */ | |
105 if (evutil_getenv("EVENT_NOSELECT")) | |
106 return (NULL); | |
107 | |
108 if (!(sop = calloc(1, sizeof(struct selectop)))) | |
109 return (NULL); | |
110 | |
111 select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask)); | |
112 | |
113 evsignal_init(base); | |
114 | |
115 return (sop); | |
116 } | |
117 | |
118 #ifdef CHECK_INVARIANTS | |
119 static void | |
120 check_selectop(struct selectop *sop) | |
121 { | |
122 int i; | |
123 for (i = 0; i <= sop->event_fds; ++i) { | |
124 if (FD_ISSET(i, sop->event_readset_in)) { | |
125 assert(sop->event_r_by_fd[i]); | |
126 assert(sop->event_r_by_fd[i]->ev_events & EV_READ); | |
127 assert(sop->event_r_by_fd[i]->ev_fd == i); | |
128 } else { | |
129 assert(! sop->event_r_by_fd[i]); | |
130 } | |
131 if (FD_ISSET(i, sop->event_writeset_in)) { | |
132 assert(sop->event_w_by_fd[i]); | |
133 assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE); | |
134 assert(sop->event_w_by_fd[i]->ev_fd == i); | |
135 } else { | |
136 assert(! sop->event_w_by_fd[i]); | |
137 } | |
138 } | |
139 | |
140 } | |
141 #else | |
142 #define check_selectop(sop) do { (void) sop; } while (0) | |
143 #endif | |
144 | |
145 static int | |
146 select_dispatch(struct event_base *base, void *arg, struct timeval *tv) | |
147 { | |
148 int res, i, j; | |
149 struct selectop *sop = arg; | |
150 | |
151 check_selectop(sop); | |
152 | |
153 memcpy(sop->event_readset_out, sop->event_readset_in, | |
154 sop->event_fdsz); | |
155 memcpy(sop->event_writeset_out, sop->event_writeset_in, | |
156 sop->event_fdsz); | |
157 | |
158 res = select(sop->event_fds + 1, sop->event_readset_out, | |
159 sop->event_writeset_out, NULL, tv); | |
160 | |
161 check_selectop(sop); | |
162 | |
163 if (res == -1) { | |
164 if (errno != EINTR) { | |
165 event_warn("select"); | |
166 return (-1); | |
167 } | |
168 | |
169 evsignal_process(base); | |
170 return (0); | |
171 } else if (base->sig.evsignal_caught) { | |
172 evsignal_process(base); | |
173 } | |
174 | |
175 event_debug(("%s: select reports %d", __func__, res)); | |
176 | |
177 check_selectop(sop); | |
178 i = random() % (sop->event_fds+1); | |
179 for (j = 0; j <= sop->event_fds; ++j) { | |
180 struct event *r_ev = NULL, *w_ev = NULL; | |
181 if (++i >= sop->event_fds+1) | |
182 i = 0; | |
183 | |
184 res = 0; | |
185 if (FD_ISSET(i, sop->event_readset_out)) { | |
186 r_ev = sop->event_r_by_fd[i]; | |
187 res |= EV_READ; | |
188 } | |
189 if (FD_ISSET(i, sop->event_writeset_out)) { | |
190 w_ev = sop->event_w_by_fd[i]; | |
191 res |= EV_WRITE; | |
192 } | |
193 if (r_ev && (res & r_ev->ev_events)) { | |
194 event_active(r_ev, res & r_ev->ev_events, 1); | |
195 } | |
196 if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { | |
197 event_active(w_ev, res & w_ev->ev_events, 1); | |
198 } | |
199 } | |
200 check_selectop(sop); | |
201 | |
202 return (0); | |
203 } | |
204 | |
205 | |
206 static int | |
207 select_resize(struct selectop *sop, int fdsz) | |
208 { | |
209 int n_events, n_events_old; | |
210 | |
211 fd_set *readset_in = NULL; | |
212 fd_set *writeset_in = NULL; | |
213 fd_set *readset_out = NULL; | |
214 fd_set *writeset_out = NULL; | |
215 struct event **r_by_fd = NULL; | |
216 struct event **w_by_fd = NULL; | |
217 | |
218 n_events = (fdsz/sizeof(fd_mask)) * NFDBITS; | |
219 n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS; | |
220 | |
221 if (sop->event_readset_in) | |
222 check_selectop(sop); | |
223 | |
224 if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL) | |
225 goto error; | |
226 sop->event_readset_in = readset_in; | |
227 if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL) | |
228 goto error; | |
229 sop->event_readset_out = readset_out; | |
230 if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL) | |
231 goto error; | |
232 sop->event_writeset_in = writeset_in; | |
233 if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL) | |
234 goto error; | |
235 sop->event_writeset_out = writeset_out; | |
236 if ((r_by_fd = realloc(sop->event_r_by_fd, | |
237 n_events*sizeof(struct event*))) == NULL) | |
238 goto error; | |
239 sop->event_r_by_fd = r_by_fd; | |
240 if ((w_by_fd = realloc(sop->event_w_by_fd, | |
241 n_events * sizeof(struct event*))) == NULL) | |
242 goto error; | |
243 sop->event_w_by_fd = w_by_fd; | |
244 | |
245 memset((char *)sop->event_readset_in + sop->event_fdsz, 0, | |
246 fdsz - sop->event_fdsz); | |
247 memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, | |
248 fdsz - sop->event_fdsz); | |
249 memset(sop->event_r_by_fd + n_events_old, 0, | |
250 (n_events-n_events_old) * sizeof(struct event*)); | |
251 memset(sop->event_w_by_fd + n_events_old, 0, | |
252 (n_events-n_events_old) * sizeof(struct event*)); | |
253 | |
254 sop->event_fdsz = fdsz; | |
255 check_selectop(sop); | |
256 | |
257 return (0); | |
258 | |
259 error: | |
260 event_warn("malloc"); | |
261 return (-1); | |
262 } | |
263 | |
264 | |
265 static int | |
266 select_add(void *arg, struct event *ev) | |
267 { | |
268 struct selectop *sop = arg; | |
269 | |
270 if (ev->ev_events & EV_SIGNAL) | |
271 return (evsignal_add(ev)); | |
272 | |
273 check_selectop(sop); | |
274 /* | |
275 * Keep track of the highest fd, so that we can calculate the size | |
276 * of the fd_sets for select(2) | |
277 */ | |
278 if (sop->event_fds < ev->ev_fd) { | |
279 int fdsz = sop->event_fdsz; | |
280 | |
281 if (fdsz < sizeof(fd_mask)) | |
282 fdsz = sizeof(fd_mask); | |
283 | |
284 while (fdsz < | |
285 (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask))) | |
286 fdsz *= 2; | |
287 | |
288 if (fdsz != sop->event_fdsz) { | |
289 if (select_resize(sop, fdsz)) { | |
290 check_selectop(sop); | |
291 return (-1); | |
292 } | |
293 } | |
294 | |
295 sop->event_fds = ev->ev_fd; | |
296 } | |
297 | |
298 if (ev->ev_events & EV_READ) { | |
299 FD_SET(ev->ev_fd, sop->event_readset_in); | |
300 sop->event_r_by_fd[ev->ev_fd] = ev; | |
301 } | |
302 if (ev->ev_events & EV_WRITE) { | |
303 FD_SET(ev->ev_fd, sop->event_writeset_in); | |
304 sop->event_w_by_fd[ev->ev_fd] = ev; | |
305 } | |
306 check_selectop(sop); | |
307 | |
308 return (0); | |
309 } | |
310 | |
311 /* | |
312 * Nothing to be done here. | |
313 */ | |
314 | |
315 static int | |
316 select_del(void *arg, struct event *ev) | |
317 { | |
318 struct selectop *sop = arg; | |
319 | |
320 check_selectop(sop); | |
321 if (ev->ev_events & EV_SIGNAL) | |
322 return (evsignal_del(ev)); | |
323 | |
324 if (sop->event_fds < ev->ev_fd) { | |
325 check_selectop(sop); | |
326 return (0); | |
327 } | |
328 | |
329 if (ev->ev_events & EV_READ) { | |
330 FD_CLR(ev->ev_fd, sop->event_readset_in); | |
331 sop->event_r_by_fd[ev->ev_fd] = NULL; | |
332 } | |
333 | |
334 if (ev->ev_events & EV_WRITE) { | |
335 FD_CLR(ev->ev_fd, sop->event_writeset_in); | |
336 sop->event_w_by_fd[ev->ev_fd] = NULL; | |
337 } | |
338 | |
339 check_selectop(sop); | |
340 return (0); | |
341 } | |
342 | |
343 static void | |
344 select_dealloc(struct event_base *base, void *arg) | |
345 { | |
346 struct selectop *sop = arg; | |
347 | |
348 evsignal_dealloc(base); | |
349 if (sop->event_readset_in) | |
350 free(sop->event_readset_in); | |
351 if (sop->event_writeset_in) | |
352 free(sop->event_writeset_in); | |
353 if (sop->event_readset_out) | |
354 free(sop->event_readset_out); | |
355 if (sop->event_writeset_out) | |
356 free(sop->event_writeset_out); | |
357 if (sop->event_r_by_fd) | |
358 free(sop->event_r_by_fd); | |
359 if (sop->event_w_by_fd) | |
360 free(sop->event_w_by_fd); | |
361 | |
362 memset(sop, 0, sizeof(struct selectop)); | |
363 free(sop); | |
364 } | |
OLD | NEW |