1 /* $NetBSD: select.c,v 1.2 2013/04/11 16:56:41 christos Exp $ */ 2 /* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ 3 4 /* 5 * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> 6 * Copyright 2007-2012 Niels Provos and Nick Mathewson 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 #include "event2/event-config.h" 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: select.c,v 1.2 2013/04/11 16:56:41 christos Exp $"); 33 34 #include <sys/types.h> 35 #ifdef _EVENT_HAVE_SYS_TIME_H 36 #include <sys/time.h> 37 #endif 38 #ifdef _EVENT_HAVE_SYS_SELECT_H 39 #include <sys/select.h> 40 #endif 41 #include <sys/queue.h> 42 #include <signal.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <errno.h> 48 49 #include "event-internal.h" 50 #include "evsignal-internal.h" 51 #include "event2/thread.h" 52 #include "evthread-internal.h" 53 #include "log-internal.h" 54 #include "evmap-internal.h" 55 56 #ifndef _EVENT_HAVE_FD_MASK 57 /* This type is mandatory, but Android doesn't define it. */ 58 typedef unsigned long fd_mask; 59 #endif 60 61 #ifndef NFDBITS 62 #define NFDBITS (sizeof(fd_mask)*8) 63 #endif 64 65 /* Divide positive x by y, rounding up. */ 66 #define DIV_ROUNDUP(x, y) (((x)+((y)-1))/(y)) 67 68 /* How many bytes to allocate for N fds? */ 69 #define SELECT_ALLOC_SIZE(n) \ 70 (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask)) 71 72 struct selectop { 73 int event_fds; /* Highest fd in fd set */ 74 int event_fdsz; 75 int resize_out_sets; 76 fd_set *event_readset_in; 77 fd_set *event_writeset_in; 78 fd_set *event_readset_out; 79 fd_set *event_writeset_out; 80 }; 81 82 static void *select_init(struct event_base *); 83 static int select_add(struct event_base *, int, short old, short events, void*); 84 static int select_del(struct event_base *, int, short old, short events, void*); 85 static int select_dispatch(struct event_base *, struct timeval *); 86 static void select_dealloc(struct event_base *); 87 88 const struct eventop selectops = { 89 "select", 90 select_init, 91 select_add, 92 select_del, 93 select_dispatch, 94 select_dealloc, 95 0, /* doesn't need reinit. */ 96 EV_FEATURE_FDS, 97 0, 98 }; 99 100 static int select_resize(struct selectop *sop, int fdsz); 101 static void select_free_selectop(struct selectop *sop); 102 103 static void * 104 select_init(struct event_base *base) 105 { 106 struct selectop *sop; 107 108 if (!(sop = mm_calloc(1, sizeof(struct selectop)))) 109 return (NULL); 110 111 if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) { 112 select_free_selectop(sop); 113 return (NULL); 114 } 115 116 evsig_init(base); 117 118 return (sop); 119 } 120 121 #ifdef CHECK_INVARIANTS 122 static void 123 check_selectop(struct selectop *sop) 124 { 125 /* nothing to be done here */ 126 } 127 #else 128 #define check_selectop(sop) do { (void) sop; } while (/*CONSTCOND*/0) 129 #endif 130 131 static int 132 select_dispatch(struct event_base *base, struct timeval *tv) 133 { 134 int res=0, i, j, nfds; 135 struct selectop *sop = base->evbase; 136 137 check_selectop(sop); 138 if (sop->resize_out_sets) { 139 fd_set *readset_out=NULL, *writeset_out=NULL; 140 size_t sz = sop->event_fdsz; 141 if (!(readset_out = mm_realloc(sop->event_readset_out, sz))) 142 return (-1); 143 sop->event_readset_out = readset_out; 144 if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) { 145 /* We don't free readset_out here, since it was 146 * already successfully reallocated. The next time 147 * we call select_dispatch, the realloc will be a 148 * no-op. */ 149 return (-1); 150 } 151 sop->event_writeset_out = writeset_out; 152 sop->resize_out_sets = 0; 153 } 154 155 memcpy(sop->event_readset_out, sop->event_readset_in, 156 sop->event_fdsz); 157 memcpy(sop->event_writeset_out, sop->event_writeset_in, 158 sop->event_fdsz); 159 160 nfds = sop->event_fds+1; 161 162 EVBASE_RELEASE_LOCK(base, th_base_lock); 163 164 res = select(nfds, sop->event_readset_out, 165 sop->event_writeset_out, NULL, tv); 166 167 EVBASE_ACQUIRE_LOCK(base, th_base_lock); 168 169 check_selectop(sop); 170 171 if (res == -1) { 172 if (errno != EINTR) { 173 event_warn("select"); 174 return (-1); 175 } 176 177 return (0); 178 } 179 180 event_debug(("%s: select reports %d", __func__, res)); 181 182 check_selectop(sop); 183 i = random() % nfds; 184 for (j = 0; j < nfds; ++j) { 185 if (++i >= nfds) 186 i = 0; 187 res = 0; 188 if (FD_ISSET(i, sop->event_readset_out)) 189 res |= EV_READ; 190 if (FD_ISSET(i, sop->event_writeset_out)) 191 res |= EV_WRITE; 192 193 if (res == 0) 194 continue; 195 196 evmap_io_active(base, i, res); 197 } 198 check_selectop(sop); 199 200 return (0); 201 } 202 203 static int 204 select_resize(struct selectop *sop, int fdsz) 205 { 206 fd_set *readset_in = NULL; 207 fd_set *writeset_in = NULL; 208 209 if (sop->event_readset_in) 210 check_selectop(sop); 211 212 if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL) 213 goto error; 214 sop->event_readset_in = readset_in; 215 if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) { 216 /* Note that this will leave event_readset_in expanded. 217 * That's okay; we wouldn't want to free it, since that would 218 * change the semantics of select_resize from "expand the 219 * readset_in and writeset_in, or return -1" to "expand the 220 * *set_in members, or trash them and return -1." 221 */ 222 goto error; 223 } 224 sop->event_writeset_in = writeset_in; 225 sop->resize_out_sets = 1; 226 227 memset((char *)sop->event_readset_in + sop->event_fdsz, 0, 228 fdsz - sop->event_fdsz); 229 memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, 230 fdsz - sop->event_fdsz); 231 232 sop->event_fdsz = fdsz; 233 check_selectop(sop); 234 235 return (0); 236 237 error: 238 event_warn("malloc"); 239 return (-1); 240 } 241 242 243 static int 244 select_add(struct event_base *base, int fd, short old, short events, void *p) 245 { 246 struct selectop *sop = base->evbase; 247 (void) p; 248 249 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 250 check_selectop(sop); 251 /* 252 * Keep track of the highest fd, so that we can calculate the size 253 * of the fd_sets for select(2) 254 */ 255 if (sop->event_fds < fd) { 256 int fdsz = sop->event_fdsz; 257 258 if (fdsz < (int)sizeof(fd_mask)) 259 fdsz = (int)sizeof(fd_mask); 260 261 /* In theory we should worry about overflow here. In 262 * reality, though, the highest fd on a unixy system will 263 * not overflow here. XXXX */ 264 while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1)) 265 fdsz *= 2; 266 267 if (fdsz != sop->event_fdsz) { 268 if (select_resize(sop, fdsz)) { 269 check_selectop(sop); 270 return (-1); 271 } 272 } 273 274 sop->event_fds = fd; 275 } 276 277 if (events & EV_READ) 278 FD_SET(fd, sop->event_readset_in); 279 if (events & EV_WRITE) 280 FD_SET(fd, sop->event_writeset_in); 281 check_selectop(sop); 282 283 return (0); 284 } 285 286 /* 287 * Nothing to be done here. 288 */ 289 290 static int 291 select_del(struct event_base *base, int fd, short old, short events, void *p) 292 { 293 struct selectop *sop = base->evbase; 294 (void)p; 295 296 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 297 check_selectop(sop); 298 299 if (sop->event_fds < fd) { 300 check_selectop(sop); 301 return (0); 302 } 303 304 if (events & EV_READ) 305 FD_CLR(fd, sop->event_readset_in); 306 307 if (events & EV_WRITE) 308 FD_CLR(fd, sop->event_writeset_in); 309 310 check_selectop(sop); 311 return (0); 312 } 313 314 static void 315 select_free_selectop(struct selectop *sop) 316 { 317 if (sop->event_readset_in) 318 mm_free(sop->event_readset_in); 319 if (sop->event_writeset_in) 320 mm_free(sop->event_writeset_in); 321 if (sop->event_readset_out) 322 mm_free(sop->event_readset_out); 323 if (sop->event_writeset_out) 324 mm_free(sop->event_writeset_out); 325 326 memset(sop, 0, sizeof(struct selectop)); 327 mm_free(sop); 328 } 329 330 static void 331 select_dealloc(struct event_base *base) 332 { 333 evsig_dealloc(base); 334 335 select_free_selectop(base->evbase); 336 } 337