1/* $NetBSD$ */ 2/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ 3 4/* 5 * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30#ifdef HAVE_CONFIG_H 31#include "config.h" 32#endif 33 34#include <sys/types.h> 35#ifdef HAVE_SYS_TIME_H 36#include <sys/time.h> 37#else 38#include <sys/_time.h> 39#endif 40#ifdef HAVE_SYS_SELECT_H 41#include <sys/select.h> 42#endif 43#include <sys/queue.h> 44#include <signal.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49#include <errno.h> 50#ifdef CHECK_INVARIANTS 51#include <assert.h> 52#endif 53 54#include "event.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 63struct selectop { 64 int event_fds; /* Highest fd in fd set */ 65 int event_fdsz; 66 fd_set *event_readset_in; 67 fd_set *event_writeset_in; 68 fd_set *event_readset_out; 69 fd_set *event_writeset_out; 70 struct event **event_r_by_fd; 71 struct event **event_w_by_fd; 72}; 73 74static void *select_init (struct event_base *); 75static int select_add (void *, struct event *); 76static int select_del (void *, struct event *); 77static int select_dispatch (struct event_base *, void *, struct timeval *); 78static void select_dealloc (struct event_base *, void *); 79 80const struct eventop selectops = { 81 "select", 82 select_init, 83 select_add, 84 select_del, 85 select_dispatch, 86 select_dealloc, 87 0 88}; 89 90static int select_resize(struct selectop *sop, int fdsz); 91 92static void * 93select_init(struct event_base *base) 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(base); 107 108 return (sop); 109} 110 111#ifdef CHECK_INVARIANTS 112static void 113check_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 138static int 139select_dispatch(struct event_base *base, void *arg, struct timeval *tv) 140{ 141 int res, i, j; 142 struct selectop *sop = arg; 143 144 check_selectop(sop); 145 146 memcpy(sop->event_readset_out, sop->event_readset_in, 147 sop->event_fdsz); 148 memcpy(sop->event_writeset_out, sop->event_writeset_in, 149 sop->event_fdsz); 150 151 res = select(sop->event_fds + 1, sop->event_readset_out, 152 sop->event_writeset_out, NULL, tv); 153 154 check_selectop(sop); 155 156 if (res == -1) { 157 if (errno != EINTR) { 158 event_warn("select"); 159 return (-1); 160 } 161 162 evsignal_process(base); 163 return (0); 164 } else if (base->sig.evsignal_caught) { 165 evsignal_process(base); 166 } 167 168 event_debug(("%s: select reports %d", __func__, res)); 169 170 check_selectop(sop); 171 i = random() % (sop->event_fds+1); 172 for (j = 0; j <= sop->event_fds; ++j) { 173 struct event *r_ev = NULL, *w_ev = NULL; 174 if (++i >= sop->event_fds+1) 175 i = 0; 176 177 res = 0; 178 if (FD_ISSET(i, sop->event_readset_out)) { 179 r_ev = sop->event_r_by_fd[i]; 180 res |= EV_READ; 181 } 182 if (FD_ISSET(i, sop->event_writeset_out)) { 183 w_ev = sop->event_w_by_fd[i]; 184 res |= EV_WRITE; 185 } 186 if (r_ev && (res & r_ev->ev_events)) { 187 event_active(r_ev, res & r_ev->ev_events, 1); 188 } 189 if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { 190 event_active(w_ev, res & w_ev->ev_events, 1); 191 } 192 } 193 check_selectop(sop); 194 195 return (0); 196} 197 198 199static int 200select_resize(struct selectop *sop, int fdsz) 201{ 202 int n_events, n_events_old; 203 204 fd_set *readset_in = NULL; 205 fd_set *writeset_in = NULL; 206 fd_set *readset_out = NULL; 207 fd_set *writeset_out = NULL; 208 struct event **r_by_fd = NULL; 209 struct event **w_by_fd = NULL; 210 211 n_events = (fdsz/sizeof(fd_mask)) * NFDBITS; 212 n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS; 213 214 if (sop->event_readset_in) 215 check_selectop(sop); 216 217 if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL) 218 goto error; 219 sop->event_readset_in = readset_in; 220 if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL) 221 goto error; 222 sop->event_readset_out = readset_out; 223 if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL) 224 goto error; 225 sop->event_writeset_in = writeset_in; 226 if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL) 227 goto error; 228 sop->event_writeset_out = writeset_out; 229 if ((r_by_fd = realloc(sop->event_r_by_fd, 230 n_events*sizeof(struct event*))) == NULL) 231 goto error; 232 sop->event_r_by_fd = r_by_fd; 233 if ((w_by_fd = realloc(sop->event_w_by_fd, 234 n_events * sizeof(struct event*))) == NULL) 235 goto error; 236 sop->event_w_by_fd = w_by_fd; 237 238 memset((char *)sop->event_readset_in + sop->event_fdsz, 0, 239 fdsz - sop->event_fdsz); 240 memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, 241 fdsz - sop->event_fdsz); 242 memset(sop->event_r_by_fd + n_events_old, 0, 243 (n_events-n_events_old) * sizeof(struct event*)); 244 memset(sop->event_w_by_fd + n_events_old, 0, 245 (n_events-n_events_old) * sizeof(struct event*)); 246 247 sop->event_fdsz = fdsz; 248 check_selectop(sop); 249 250 return (0); 251 252 error: 253 event_warn("malloc"); 254 return (-1); 255} 256 257 258static int 259select_add(void *arg, struct event *ev) 260{ 261 struct selectop *sop = arg; 262 263 if (ev->ev_events & EV_SIGNAL) 264 return (evsignal_add(ev)); 265 266 check_selectop(sop); 267 /* 268 * Keep track of the highest fd, so that we can calculate the size 269 * of the fd_sets for select(2) 270 */ 271 if (sop->event_fds < ev->ev_fd) { 272 int fdsz = sop->event_fdsz; 273 274 if (fdsz < sizeof(fd_mask)) 275 fdsz = sizeof(fd_mask); 276 277 while (fdsz < 278 (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask))) 279 fdsz *= 2; 280 281 if (fdsz != sop->event_fdsz) { 282 if (select_resize(sop, fdsz)) { 283 check_selectop(sop); 284 return (-1); 285 } 286 } 287 288 sop->event_fds = ev->ev_fd; 289 } 290 291 if (ev->ev_events & EV_READ) { 292 FD_SET(ev->ev_fd, sop->event_readset_in); 293 sop->event_r_by_fd[ev->ev_fd] = ev; 294 } 295 if (ev->ev_events & EV_WRITE) { 296 FD_SET(ev->ev_fd, sop->event_writeset_in); 297 sop->event_w_by_fd[ev->ev_fd] = ev; 298 } 299 check_selectop(sop); 300 301 return (0); 302} 303 304/* 305 * Nothing to be done here. 306 */ 307 308static int 309select_del(void *arg, struct event *ev) 310{ 311 struct selectop *sop = arg; 312 313 check_selectop(sop); 314 if (ev->ev_events & EV_SIGNAL) 315 return (evsignal_del(ev)); 316 317 if (sop->event_fds < ev->ev_fd) { 318 check_selectop(sop); 319 return (0); 320 } 321 322 if (ev->ev_events & EV_READ) { 323 FD_CLR(ev->ev_fd, sop->event_readset_in); 324 sop->event_r_by_fd[ev->ev_fd] = NULL; 325 } 326 327 if (ev->ev_events & EV_WRITE) { 328 FD_CLR(ev->ev_fd, sop->event_writeset_in); 329 sop->event_w_by_fd[ev->ev_fd] = NULL; 330 } 331 332 check_selectop(sop); 333 return (0); 334} 335 336static void 337select_dealloc(struct event_base *base, void *arg) 338{ 339 struct selectop *sop = arg; 340 341 evsignal_dealloc(base); 342 if (sop->event_readset_in) 343 free(sop->event_readset_in); 344 if (sop->event_writeset_in) 345 free(sop->event_writeset_in); 346 if (sop->event_readset_out) 347 free(sop->event_readset_out); 348 if (sop->event_writeset_out) 349 free(sop->event_writeset_out); 350 if (sop->event_r_by_fd) 351 free(sop->event_r_by_fd); 352 if (sop->event_w_by_fd) 353 free(sop->event_w_by_fd); 354 355 memset(sop, 0, sizeof(struct selectop)); 356 free(sop); 357} 358