1 /* 2 * libev select fd activity backend 3 * 4 * Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann <[email protected]> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without modifica- 8 * tion, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 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 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 * OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Alternatively, the contents of this file may be used under the terms of 29 * the GNU General Public License ("GPL") version 2 or any later version, 30 * in which case the provisions of the GPL are applicable instead of 31 * the above. If you wish to allow the use of your version of this file 32 * only under the terms of the GPL and not to allow others to use your 33 * version of this file under the BSD license, indicate your decision 34 * by deleting the provisions above and replace them with the notice 35 * and other provisions required by the GPL. If you do not delete the 36 * provisions above, a recipient may use your version of this file under 37 * either the BSD or the GPL. 38 */ 39 40 #ifndef _WIN32 41 /* for unix systems */ 42 # include <inttypes.h> 43 # ifndef __hpux 44 /* for REAL unix systems */ 45 # include <sys/select.h> 46 # endif 47 #endif 48 49 #ifndef EV_SELECT_USE_FD_SET 50 # ifdef NFDBITS 51 # define EV_SELECT_USE_FD_SET 0 52 # else 53 # define EV_SELECT_USE_FD_SET 1 54 # endif 55 #endif 56 57 #if EV_SELECT_IS_WINSOCKET 58 # undef EV_SELECT_USE_FD_SET 59 # define EV_SELECT_USE_FD_SET 1 60 # undef NFDBITS 61 # define NFDBITS 0 62 #endif 63 64 #if !EV_SELECT_USE_FD_SET 65 # define NFDBYTES (NFDBITS / 8) 66 #endif 67 68 #include <string.h> 69 70 static void 71 select_modify (EV_P_ int fd, int oev, int nev) 72 { 73 if (oev == nev) 74 return; 75 76 { 77 #if EV_SELECT_USE_FD_SET 78 79 #if EV_SELECT_IS_WINSOCKET 80 SOCKET handle = anfds [fd].handle; 81 #else 82 int handle = fd; 83 #endif 84 85 assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE)); 86 87 /* FD_SET is broken on windows (it adds the fd to a set twice or more, 88 * which eventually leads to overflows). Need to call it only on changes. 89 */ 90 #if EV_SELECT_IS_WINSOCKET 91 if ((oev ^ nev) & EV_READ) 92 #endif 93 if (nev & EV_READ) 94 FD_SET (handle, (fd_set *)vec_ri); 95 else 96 FD_CLR (handle, (fd_set *)vec_ri); 97 98 #if EV_SELECT_IS_WINSOCKET 99 if ((oev ^ nev) & EV_WRITE) 100 #endif 101 if (nev & EV_WRITE) 102 FD_SET (handle, (fd_set *)vec_wi); 103 else 104 FD_CLR (handle, (fd_set *)vec_wi); 105 106 #else 107 108 int word = fd / NFDBITS; 109 fd_mask mask = 1UL << (fd % NFDBITS); 110 111 if (expect_false (vec_max <= word)) 112 { 113 int new_max = word + 1; 114 115 vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES); 116 vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */ 117 vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES); 118 vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */ 119 #ifdef _WIN32 120 vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */ 121 #endif 122 123 for (; vec_max < new_max; ++vec_max) 124 ((fd_mask *)vec_ri) [vec_max] = 125 ((fd_mask *)vec_wi) [vec_max] = 0; 126 } 127 128 ((fd_mask *)vec_ri) [word] |= mask; 129 if (!(nev & EV_READ)) 130 ((fd_mask *)vec_ri) [word] &= ~mask; 131 132 ((fd_mask *)vec_wi) [word] |= mask; 133 if (!(nev & EV_WRITE)) 134 ((fd_mask *)vec_wi) [word] &= ~mask; 135 #endif 136 } 137 } 138 139 static void 140 select_poll (EV_P_ ev_tstamp timeout) 141 { 142 struct timeval tv; 143 int res; 144 int fd_setsize; 145 146 EV_RELEASE_CB; 147 EV_TV_SET (tv, timeout); 148 149 #if EV_SELECT_USE_FD_SET 150 fd_setsize = sizeof (fd_set); 151 #else 152 fd_setsize = vec_max * NFDBYTES; 153 #endif 154 155 memcpy (vec_ro, vec_ri, fd_setsize); 156 memcpy (vec_wo, vec_wi, fd_setsize); 157 158 #ifdef _WIN32 159 /* pass in the write set as except set. 160 * the idea behind this is to work around a windows bug that causes 161 * errors to be reported as an exception and not by setting 162 * the writable bit. this is so uncontrollably lame. 163 */ 164 memcpy (vec_eo, vec_wi, fd_setsize); 165 res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv); 166 #elif EV_SELECT_USE_FD_SET 167 fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE; 168 res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 169 #else 170 res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 171 #endif 172 EV_ACQUIRE_CB; 173 174 if (expect_false (res < 0)) 175 { 176 #if EV_SELECT_IS_WINSOCKET 177 errno = WSAGetLastError (); 178 #endif 179 #ifdef WSABASEERR 180 /* on windows, select returns incompatible error codes, fix this */ 181 if (errno >= WSABASEERR && errno < WSABASEERR + 1000) 182 if (errno == WSAENOTSOCK) 183 errno = EBADF; 184 else 185 errno -= WSABASEERR; 186 #endif 187 188 #ifdef _WIN32 189 /* select on windows erroneously returns EINVAL when no fd sets have been 190 * provided (this is documented). what microsoft doesn't tell you that this bug 191 * exists even when the fd sets _are_ provided, so we have to check for this bug 192 * here and emulate by sleeping manually. 193 * we also get EINVAL when the timeout is invalid, but we ignore this case here 194 * and assume that EINVAL always means: you have to wait manually. 195 */ 196 if (errno == EINVAL) 197 { 198 ev_sleep (timeout); 199 return; 200 } 201 #endif 202 203 if (errno == EBADF) 204 fd_ebadf (EV_A); 205 else if (errno == ENOMEM && !syserr_cb) 206 fd_enomem (EV_A); 207 else if (errno != EINTR) 208 ev_syserr ("(libev) select"); 209 210 return; 211 } 212 213 #if EV_SELECT_USE_FD_SET 214 215 { 216 int fd; 217 218 for (fd = 0; fd < anfdmax; ++fd) 219 if (anfds [fd].events) 220 { 221 int events = 0; 222 #if EV_SELECT_IS_WINSOCKET 223 SOCKET handle = anfds [fd].handle; 224 #else 225 int handle = fd; 226 #endif 227 228 if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ; 229 if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE; 230 #ifdef _WIN32 231 if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE; 232 #endif 233 234 if (expect_true (events)) 235 fd_event (EV_A_ fd, events); 236 } 237 } 238 239 #else 240 241 { 242 int word, bit; 243 for (word = vec_max; word--; ) 244 { 245 fd_mask word_r = ((fd_mask *)vec_ro) [word]; 246 fd_mask word_w = ((fd_mask *)vec_wo) [word]; 247 #ifdef _WIN32 248 word_w |= ((fd_mask *)vec_eo) [word]; 249 #endif 250 251 if (word_r || word_w) 252 for (bit = NFDBITS; bit--; ) 253 { 254 fd_mask mask = 1UL << bit; 255 int events = 0; 256 257 events |= word_r & mask ? EV_READ : 0; 258 events |= word_w & mask ? EV_WRITE : 0; 259 260 if (expect_true (events)) 261 fd_event (EV_A_ word * NFDBITS + bit, events); 262 } 263 } 264 } 265 266 #endif 267 } 268 269 int inline_size 270 select_init (EV_P_ int flags) 271 { 272 backend_fudge = 0.; /* posix says this is zero */ 273 backend_modify = select_modify; 274 backend_poll = select_poll; 275 276 #if EV_SELECT_USE_FD_SET 277 vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri); 278 vec_ro = ev_malloc (sizeof (fd_set)); 279 vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi); 280 vec_wo = ev_malloc (sizeof (fd_set)); 281 #ifdef _WIN32 282 vec_eo = ev_malloc (sizeof (fd_set)); 283 #endif 284 #else 285 vec_max = 0; 286 vec_ri = 0; 287 vec_ro = 0; 288 vec_wi = 0; 289 vec_wo = 0; 290 #ifdef _WIN32 291 vec_eo = 0; 292 #endif 293 #endif 294 295 return EVBACKEND_SELECT; 296 } 297 298 void inline_size 299 select_destroy (EV_P) 300 { 301 ev_free (vec_ri); 302 ev_free (vec_ro); 303 ev_free (vec_wi); 304 ev_free (vec_wo); 305 #ifdef _WIN32 306 ev_free (vec_eo); 307 #endif 308 } 309 310 311