| 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 |