1 /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ 2 3 /* 4 * Copyright 2000-2003 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 <poll.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 #ifdef CHECK_INVARIANTS 49 #include <assert.h> 50 #endif 51 52 #include "event.h" 53 #include "event-internal.h" 54 #include "evsignal.h" 55 #include "log.h" 56 57 extern volatile sig_atomic_t evsignal_caught; 58 59 struct pollop { 60 int event_count; /* Highest number alloc */ 61 int nfds; /* Size of event_* */ 62 int fd_count; /* Size of idxplus1_by_fd */ 63 struct pollfd *event_set; 64 struct event **event_r_back; 65 struct event **event_w_back; 66 int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so 67 * that 0 (which is easy to memset) can mean 68 * "no entry." */ 69 }; 70 71 void *poll_init (void); 72 int poll_add (void *, struct event *); 73 int poll_del (void *, struct event *); 74 int poll_recalc (struct event_base *, void *, int); 75 int poll_dispatch (struct event_base *, void *, struct timeval *); 76 void poll_dealloc (void *); 77 78 const struct eventop pollops = { 79 "poll", 80 poll_init, 81 poll_add, 82 poll_del, 83 poll_recalc, 84 poll_dispatch, 85 poll_dealloc 86 }; 87 88 void * 89 poll_init(void) 90 { 91 struct pollop *pollop; 92 93 /* Disable poll when this environment variable is set */ 94 if (getenv("EVENT_NOPOLL")) 95 return (NULL); 96 97 if (!(pollop = calloc(1, sizeof(struct pollop)))) 98 return (NULL); 99 100 evsignal_init(); 101 102 return (pollop); 103 } 104 105 /* 106 * Called with the highest fd that we know about. If it is 0, completely 107 * recalculate everything. 108 */ 109 110 int 111 poll_recalc(struct event_base *base, void *arg, int max) 112 { 113 return (0); 114 } 115 116 #ifdef CHECK_INVARIANTS 117 static void 118 poll_check_ok(struct pollop *pop) 119 { 120 int i, idx; 121 struct event *ev; 122 123 for (i = 0; i < pop->fd_count; ++i) { 124 idx = pop->idxplus1_by_fd[i]-1; 125 if (idx < 0) 126 continue; 127 assert(pop->event_set[idx].fd == i); 128 if (pop->event_set[idx].events & POLLIN) { 129 ev = pop->event_r_back[idx]; 130 assert(ev); 131 assert(ev->ev_events & EV_READ); 132 assert(ev->ev_fd == i); 133 } 134 if (pop->event_set[idx].events & POLLOUT) { 135 ev = pop->event_w_back[idx]; 136 assert(ev); 137 assert(ev->ev_events & EV_WRITE); 138 assert(ev->ev_fd == i); 139 } 140 } 141 for (i = 0; i < pop->nfds; ++i) { 142 struct pollfd *pfd = &pop->event_set[i]; 143 assert(pop->idxplus1_by_fd[pfd->fd] == i+1); 144 } 145 } 146 #else 147 #define poll_check_ok(pop) 148 #endif 149 150 int 151 poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) 152 { 153 int res, i, sec, nfds; 154 struct pollop *pop = arg; 155 156 poll_check_ok(pop); 157 sec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; 158 nfds = pop->nfds; 159 res = poll(pop->event_set, nfds, sec); 160 161 if (res == -1) { 162 if (errno != EINTR) { 163 event_warn("poll"); 164 return (-1); 165 } 166 167 evsignal_process(); 168 return (0); 169 } else if (evsignal_caught) 170 evsignal_process(); 171 172 event_debug(("%s: poll reports %d", __func__, res)); 173 174 if (res == 0) 175 return (0); 176 177 for (i = 0; i < nfds; i++) { 178 int what = pop->event_set[i].revents; 179 struct event *r_ev = NULL, *w_ev = NULL; 180 if (!what) 181 continue; 182 183 res = 0; 184 185 /* If the file gets closed notify */ 186 if (what & (POLLHUP|POLLERR)) 187 what |= POLLIN|POLLOUT; 188 if (what & POLLIN) { 189 res |= EV_READ; 190 r_ev = pop->event_r_back[i]; 191 } 192 if (what & POLLOUT) { 193 res |= EV_WRITE; 194 w_ev = pop->event_w_back[i]; 195 } 196 if (res == 0) 197 continue; 198 199 if (r_ev && (res & r_ev->ev_events)) { 200 if (!(r_ev->ev_events & EV_PERSIST)) 201 event_del(r_ev); 202 event_active(r_ev, res & r_ev->ev_events, 1); 203 } 204 if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { 205 if (!(w_ev->ev_events & EV_PERSIST)) 206 event_del(w_ev); 207 event_active(w_ev, res & w_ev->ev_events, 1); 208 } 209 } 210 211 return (0); 212 } 213 214 int 215 poll_add(void *arg, struct event *ev) 216 { 217 struct pollop *pop = arg; 218 struct pollfd *pfd = NULL; 219 int i; 220 221 if (ev->ev_events & EV_SIGNAL) 222 return (evsignal_add(ev)); 223 if (!(ev->ev_events & (EV_READ|EV_WRITE))) 224 return (0); 225 226 poll_check_ok(pop); 227 if (pop->nfds + 1 >= pop->event_count) { 228 struct pollfd *tmp_event_set; 229 struct event **tmp_event_r_back; 230 struct event **tmp_event_w_back; 231 int tmp_event_count; 232 233 if (pop->event_count < 32) 234 tmp_event_count = 32; 235 else 236 tmp_event_count = pop->event_count * 2; 237 238 /* We need more file descriptors */ 239 tmp_event_set = realloc(pop->event_set, 240 tmp_event_count * sizeof(struct pollfd)); 241 if (tmp_event_set == NULL) { 242 event_warn("realloc"); 243 return (-1); 244 } 245 pop->event_set = tmp_event_set; 246 247 tmp_event_r_back = realloc(pop->event_r_back, 248 tmp_event_count * sizeof(struct event *)); 249 if (tmp_event_r_back == NULL) { 250 /* event_set overallocated; that's okay. */ 251 event_warn("realloc"); 252 return (-1); 253 } 254 pop->event_r_back = tmp_event_r_back; 255 256 tmp_event_w_back = realloc(pop->event_w_back, 257 tmp_event_count * sizeof(struct event *)); 258 if (tmp_event_w_back == NULL) { 259 /* event_set and event_r_back overallocated; that's 260 * okay. */ 261 event_warn("realloc"); 262 return (-1); 263 } 264 pop->event_w_back = tmp_event_w_back; 265 266 pop->event_count = tmp_event_count; 267 } 268 if (ev->ev_fd >= pop->fd_count) { 269 int *tmp_idxplus1_by_fd; 270 int new_count; 271 if (pop->fd_count < 32) 272 new_count = 32; 273 else 274 new_count = pop->fd_count * 2; 275 while (new_count <= ev->ev_fd) 276 new_count *= 2; 277 tmp_idxplus1_by_fd = 278 realloc(pop->idxplus1_by_fd, new_count * sizeof(int)); 279 if (tmp_idxplus1_by_fd == NULL) { 280 event_warn("realloc"); 281 return (-1); 282 } 283 pop->idxplus1_by_fd = tmp_idxplus1_by_fd; 284 memset(pop->idxplus1_by_fd + pop->fd_count, 285 0, sizeof(int)*(new_count - pop->fd_count)); 286 pop->fd_count = new_count; 287 } 288 289 i = pop->idxplus1_by_fd[ev->ev_fd] - 1; 290 if (i >= 0) { 291 pfd = &pop->event_set[i]; 292 } else { 293 i = pop->nfds++; 294 pfd = &pop->event_set[i]; 295 pfd->events = 0; 296 pfd->fd = ev->ev_fd; 297 pop->event_w_back[i] = pop->event_r_back[i] = NULL; 298 pop->idxplus1_by_fd[ev->ev_fd] = i + 1; 299 } 300 301 pfd->revents = 0; 302 if (ev->ev_events & EV_WRITE) { 303 pfd->events |= POLLOUT; 304 pop->event_w_back[i] = ev; 305 } 306 if (ev->ev_events & EV_READ) { 307 pfd->events |= POLLIN; 308 pop->event_r_back[i] = ev; 309 } 310 poll_check_ok(pop); 311 312 return (0); 313 } 314 315 /* 316 * Nothing to be done here. 317 */ 318 319 int 320 poll_del(void *arg, struct event *ev) 321 { 322 struct pollop *pop = arg; 323 struct pollfd *pfd = NULL; 324 int i; 325 326 if (ev->ev_events & EV_SIGNAL) 327 return (evsignal_del(ev)); 328 329 if (!(ev->ev_events & (EV_READ|EV_WRITE))) 330 return (0); 331 332 poll_check_ok(pop); 333 i = pop->idxplus1_by_fd[ev->ev_fd] - 1; 334 if (i < 0) 335 return (-1); 336 337 /* Do we still want to read or write? */ 338 pfd = &pop->event_set[i]; 339 if (ev->ev_events & EV_READ) { 340 pfd->events &= ~POLLIN; 341 pop->event_r_back[i] = NULL; 342 } 343 if (ev->ev_events & EV_WRITE) { 344 pfd->events &= ~POLLOUT; 345 pop->event_w_back[i] = NULL; 346 } 347 poll_check_ok(pop); 348 if (pfd->events) 349 /* Another event cares about that fd. */ 350 return (0); 351 352 /* Okay, so we aren't interested in that fd anymore. */ 353 pop->idxplus1_by_fd[ev->ev_fd] = 0; 354 355 --pop->nfds; 356 if (i != pop->nfds) { 357 /* 358 * Shift the last pollfd down into the now-unoccupied 359 * position. 360 */ 361 memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], 362 sizeof(struct pollfd)); 363 pop->event_r_back[i] = pop->event_r_back[pop->nfds]; 364 pop->event_w_back[i] = pop->event_w_back[pop->nfds]; 365 pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1; 366 } 367 368 poll_check_ok(pop); 369 return (0); 370 } 371 372 void 373 poll_dealloc(void *arg) 374 { 375 struct pollop *pop = arg; 376 377 if (pop->event_set) 378 free(pop->event_set); 379 if (pop->event_r_back) 380 free(pop->event_r_back); 381 if (pop->event_w_back) 382 free(pop->event_w_back); 383 if (pop->idxplus1_by_fd) 384 free(pop->idxplus1_by_fd); 385 386 memset(pop, 0, sizeof(struct pollop)); 387 free(pop); 388 } 389