poll.c revision 290001
1/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ 2 3/* 4 * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> 5 * Copyright 2007-2012 Niels Provos and Nick Mathewson 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#include "event2/event-config.h" 30#include "evconfig-private.h" 31 32#ifdef EVENT__HAVE_POLL 33 34#include <sys/types.h> 35#ifdef EVENT__HAVE_SYS_TIME_H 36#include <sys/time.h> 37#endif 38#include <sys/queue.h> 39#include <poll.h> 40#include <signal.h> 41#include <limits.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46#include <errno.h> 47 48#include "event-internal.h" 49#include "evsignal-internal.h" 50#include "log-internal.h" 51#include "evmap-internal.h" 52#include "event2/thread.h" 53#include "evthread-internal.h" 54#include "time-internal.h" 55 56struct pollidx { 57 int idxplus1; 58}; 59 60struct pollop { 61 int event_count; /* Highest number alloc */ 62 int nfds; /* Highest number used */ 63 int realloc_copy; /* True iff we must realloc 64 * event_set_copy */ 65 struct pollfd *event_set; 66 struct pollfd *event_set_copy; 67}; 68 69static void *poll_init(struct event_base *); 70static int poll_add(struct event_base *, int, short old, short events, void *idx); 71static int poll_del(struct event_base *, int, short old, short events, void *idx); 72static int poll_dispatch(struct event_base *, struct timeval *); 73static void poll_dealloc(struct event_base *); 74 75const struct eventop pollops = { 76 "poll", 77 poll_init, 78 poll_add, 79 poll_del, 80 poll_dispatch, 81 poll_dealloc, 82 0, /* doesn't need_reinit */ 83 EV_FEATURE_FDS, 84 sizeof(struct pollidx), 85}; 86 87static void * 88poll_init(struct event_base *base) 89{ 90 struct pollop *pollop; 91 92 if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) 93 return (NULL); 94 95 evsig_init_(base); 96 97 evutil_weakrand_seed_(&base->weakrand_seed, 0); 98 99 return (pollop); 100} 101 102#ifdef CHECK_INVARIANTS 103static void 104poll_check_ok(struct pollop *pop) 105{ 106 int i, idx; 107 struct event *ev; 108 109 for (i = 0; i < pop->fd_count; ++i) { 110 idx = pop->idxplus1_by_fd[i]-1; 111 if (idx < 0) 112 continue; 113 EVUTIL_ASSERT(pop->event_set[idx].fd == i); 114 } 115 for (i = 0; i < pop->nfds; ++i) { 116 struct pollfd *pfd = &pop->event_set[i]; 117 EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); 118 } 119} 120#else 121#define poll_check_ok(pop) 122#endif 123 124static int 125poll_dispatch(struct event_base *base, struct timeval *tv) 126{ 127 int res, i, j, nfds; 128 long msec = -1; 129 struct pollop *pop = base->evbase; 130 struct pollfd *event_set; 131 132 poll_check_ok(pop); 133 134 nfds = pop->nfds; 135 136#ifndef EVENT__DISABLE_THREAD_SUPPORT 137 if (base->th_base_lock) { 138 /* If we're using this backend in a multithreaded setting, 139 * then we need to work on a copy of event_set, so that we can 140 * let other threads modify the main event_set while we're 141 * polling. If we're not multithreaded, then we'll skip the 142 * copy step here to save memory and time. */ 143 if (pop->realloc_copy) { 144 struct pollfd *tmp = mm_realloc(pop->event_set_copy, 145 pop->event_count * sizeof(struct pollfd)); 146 if (tmp == NULL) { 147 event_warn("realloc"); 148 return -1; 149 } 150 pop->event_set_copy = tmp; 151 pop->realloc_copy = 0; 152 } 153 memcpy(pop->event_set_copy, pop->event_set, 154 sizeof(struct pollfd)*nfds); 155 event_set = pop->event_set_copy; 156 } else { 157 event_set = pop->event_set; 158 } 159#else 160 event_set = pop->event_set; 161#endif 162 163 if (tv != NULL) { 164 msec = evutil_tv_to_msec_(tv); 165 if (msec < 0 || msec > INT_MAX) 166 msec = INT_MAX; 167 } 168 169 EVBASE_RELEASE_LOCK(base, th_base_lock); 170 171 res = poll(event_set, nfds, msec); 172 173 EVBASE_ACQUIRE_LOCK(base, th_base_lock); 174 175 if (res == -1) { 176 if (errno != EINTR) { 177 event_warn("poll"); 178 return (-1); 179 } 180 181 return (0); 182 } 183 184 event_debug(("%s: poll reports %d", __func__, res)); 185 186 if (res == 0 || nfds == 0) 187 return (0); 188 189 i = evutil_weakrand_range_(&base->weakrand_seed, nfds); 190 for (j = 0; j < nfds; j++) { 191 int what; 192 if (++i == nfds) 193 i = 0; 194 what = event_set[i].revents; 195 if (!what) 196 continue; 197 198 res = 0; 199 200 /* If the file gets closed notify */ 201 if (what & (POLLHUP|POLLERR)) 202 what |= POLLIN|POLLOUT; 203 if (what & POLLIN) 204 res |= EV_READ; 205 if (what & POLLOUT) 206 res |= EV_WRITE; 207 if (res == 0) 208 continue; 209 210 evmap_io_active_(base, event_set[i].fd, res); 211 } 212 213 return (0); 214} 215 216static int 217poll_add(struct event_base *base, int fd, short old, short events, void *idx_) 218{ 219 struct pollop *pop = base->evbase; 220 struct pollfd *pfd = NULL; 221 struct pollidx *idx = idx_; 222 int i; 223 224 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 225 if (!(events & (EV_READ|EV_WRITE))) 226 return (0); 227 228 poll_check_ok(pop); 229 if (pop->nfds + 1 >= pop->event_count) { 230 struct pollfd *tmp_event_set; 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 = mm_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 pop->event_count = tmp_event_count; 248 pop->realloc_copy = 1; 249 } 250 251 i = idx->idxplus1 - 1; 252 253 if (i >= 0) { 254 pfd = &pop->event_set[i]; 255 } else { 256 i = pop->nfds++; 257 pfd = &pop->event_set[i]; 258 pfd->events = 0; 259 pfd->fd = fd; 260 idx->idxplus1 = i + 1; 261 } 262 263 pfd->revents = 0; 264 if (events & EV_WRITE) 265 pfd->events |= POLLOUT; 266 if (events & EV_READ) 267 pfd->events |= POLLIN; 268 poll_check_ok(pop); 269 270 return (0); 271} 272 273/* 274 * Nothing to be done here. 275 */ 276 277static int 278poll_del(struct event_base *base, int fd, short old, short events, void *idx_) 279{ 280 struct pollop *pop = base->evbase; 281 struct pollfd *pfd = NULL; 282 struct pollidx *idx = idx_; 283 int i; 284 285 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 286 if (!(events & (EV_READ|EV_WRITE))) 287 return (0); 288 289 poll_check_ok(pop); 290 i = idx->idxplus1 - 1; 291 if (i < 0) 292 return (-1); 293 294 /* Do we still want to read or write? */ 295 pfd = &pop->event_set[i]; 296 if (events & EV_READ) 297 pfd->events &= ~POLLIN; 298 if (events & EV_WRITE) 299 pfd->events &= ~POLLOUT; 300 poll_check_ok(pop); 301 if (pfd->events) 302 /* Another event cares about that fd. */ 303 return (0); 304 305 /* Okay, so we aren't interested in that fd anymore. */ 306 idx->idxplus1 = 0; 307 308 --pop->nfds; 309 if (i != pop->nfds) { 310 /* 311 * Shift the last pollfd down into the now-unoccupied 312 * position. 313 */ 314 memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], 315 sizeof(struct pollfd)); 316 idx = evmap_io_get_fdinfo_(&base->io, pop->event_set[i].fd); 317 EVUTIL_ASSERT(idx); 318 EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); 319 idx->idxplus1 = i + 1; 320 } 321 322 poll_check_ok(pop); 323 return (0); 324} 325 326static void 327poll_dealloc(struct event_base *base) 328{ 329 struct pollop *pop = base->evbase; 330 331 evsig_dealloc_(base); 332 if (pop->event_set) 333 mm_free(pop->event_set); 334 if (pop->event_set_copy) 335 mm_free(pop->event_set_copy); 336 337 memset(pop, 0, sizeof(struct pollop)); 338 mm_free(pop); 339} 340 341#endif /* EVENT__HAVE_POLL */ 342