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