1 /* $NetBSD: poll.c,v 1.1.1.2 2013/04/11 16:43:26 christos Exp $ */ 2 /* $OpenBSD: poll.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: poll.c,v 1.1.1.2 2013/04/11 16:43:26 christos Exp $"); 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 55 struct pollidx { 56 int idxplus1; 57 }; 58 59 struct pollop { 60 int event_count; /* Highest number alloc */ 61 int nfds; /* Highest number used */ 62 int realloc_copy; /* True iff we must realloc 63 * event_set_copy */ 64 struct pollfd *event_set; 65 struct pollfd *event_set_copy; 66 }; 67 68 static void *poll_init(struct event_base *); 69 static int poll_add(struct event_base *, int, short old, short events, void *_idx); 70 static int poll_del(struct event_base *, int, short old, short events, void *_idx); 71 static int poll_dispatch(struct event_base *, struct timeval *); 72 static void poll_dealloc(struct event_base *); 73 74 const struct eventop pollops = { 75 "poll", 76 poll_init, 77 poll_add, 78 poll_del, 79 poll_dispatch, 80 poll_dealloc, 81 0, /* doesn't need_reinit */ 82 EV_FEATURE_FDS, 83 sizeof(struct pollidx), 84 }; 85 86 static void * 87 poll_init(struct event_base *base) 88 { 89 struct pollop *pollop; 90 91 if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) 92 return (NULL); 93 94 evsig_init(base); 95 96 return (pollop); 97 } 98 99 #ifdef CHECK_INVARIANTS 100 static void 101 poll_check_ok(struct pollop *pop) 102 { 103 int i, idx; 104 struct event *ev; 105 106 for (i = 0; i < pop->fd_count; ++i) { 107 idx = pop->idxplus1_by_fd[i]-1; 108 if (idx < 0) 109 continue; 110 EVUTIL_ASSERT(pop->event_set[idx].fd == i); 111 } 112 for (i = 0; i < pop->nfds; ++i) { 113 struct pollfd *pfd = &pop->event_set[i]; 114 EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); 115 } 116 } 117 #else 118 #define poll_check_ok(pop) 119 #endif 120 121 static int 122 poll_dispatch(struct event_base *base, struct timeval *tv) 123 { 124 int res, i, j, nfds; 125 long msec = -1; 126 struct pollop *pop = base->evbase; 127 struct pollfd *event_set; 128 129 poll_check_ok(pop); 130 131 nfds = pop->nfds; 132 133 #ifndef _EVENT_DISABLE_THREAD_SUPPORT 134 if (base->th_base_lock) { 135 /* If we're using this backend in a multithreaded setting, 136 * then we need to work on a copy of event_set, so that we can 137 * let other threads modify the main event_set while we're 138 * polling. If we're not multithreaded, then we'll skip the 139 * copy step here to save memory and time. */ 140 if (pop->realloc_copy) { 141 struct pollfd *tmp = mm_realloc(pop->event_set_copy, 142 pop->event_count * sizeof(struct pollfd)); 143 if (tmp == NULL) { 144 event_warn("realloc"); 145 return -1; 146 } 147 pop->event_set_copy = tmp; 148 pop->realloc_copy = 0; 149 } 150 memcpy(pop->event_set_copy, pop->event_set, 151 sizeof(struct pollfd)*nfds); 152 event_set = pop->event_set_copy; 153 } else { 154 event_set = pop->event_set; 155 } 156 #else 157 event_set = pop->event_set; 158 #endif 159 160 if (tv != NULL) { 161 msec = evutil_tv_to_msec(tv); 162 if (msec < 0 || msec > INT_MAX) 163 msec = INT_MAX; 164 } 165 166 EVBASE_RELEASE_LOCK(base, th_base_lock); 167 168 res = poll(event_set, nfds, msec); 169 170 EVBASE_ACQUIRE_LOCK(base, th_base_lock); 171 172 if (res == -1) { 173 if (errno != EINTR) { 174 event_warn("poll"); 175 return (-1); 176 } 177 178 return (0); 179 } 180 181 event_debug(("%s: poll reports %d", __func__, res)); 182 183 if (res == 0 || nfds == 0) 184 return (0); 185 186 i = random() % nfds; 187 for (j = 0; j < nfds; j++) { 188 int what; 189 if (++i == nfds) 190 i = 0; 191 what = event_set[i].revents; 192 if (!what) 193 continue; 194 195 res = 0; 196 197 /* If the file gets closed notify */ 198 if (what & (POLLHUP|POLLERR)) 199 what |= POLLIN|POLLOUT; 200 if (what & POLLIN) 201 res |= EV_READ; 202 if (what & POLLOUT) 203 res |= EV_WRITE; 204 if (res == 0) 205 continue; 206 207 evmap_io_active(base, event_set[i].fd, res); 208 } 209 210 return (0); 211 } 212 213 static int 214 poll_add(struct event_base *base, int fd, short old, short events, void *_idx) 215 { 216 struct pollop *pop = base->evbase; 217 struct pollfd *pfd = NULL; 218 struct pollidx *idx = _idx; 219 int i; 220 221 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 222 if (!(events & (EV_READ|EV_WRITE))) 223 return (0); 224 225 poll_check_ok(pop); 226 if (pop->nfds + 1 >= pop->event_count) { 227 struct pollfd *tmp_event_set; 228 int tmp_event_count; 229 230 if (pop->event_count < 32) 231 tmp_event_count = 32; 232 else 233 tmp_event_count = pop->event_count * 2; 234 235 /* We need more file descriptors */ 236 tmp_event_set = mm_realloc(pop->event_set, 237 tmp_event_count * sizeof(struct pollfd)); 238 if (tmp_event_set == NULL) { 239 event_warn("realloc"); 240 return (-1); 241 } 242 pop->event_set = tmp_event_set; 243 244 pop->event_count = tmp_event_count; 245 pop->realloc_copy = 1; 246 } 247 248 i = idx->idxplus1 - 1; 249 250 if (i >= 0) { 251 pfd = &pop->event_set[i]; 252 } else { 253 i = pop->nfds++; 254 pfd = &pop->event_set[i]; 255 pfd->events = 0; 256 pfd->fd = fd; 257 idx->idxplus1 = i + 1; 258 } 259 260 pfd->revents = 0; 261 if (events & EV_WRITE) 262 pfd->events |= POLLOUT; 263 if (events & EV_READ) 264 pfd->events |= POLLIN; 265 poll_check_ok(pop); 266 267 return (0); 268 } 269 270 /* 271 * Nothing to be done here. 272 */ 273 274 static int 275 poll_del(struct event_base *base, int fd, short old, short events, void *_idx) 276 { 277 struct pollop *pop = base->evbase; 278 struct pollfd *pfd = NULL; 279 struct pollidx *idx = _idx; 280 int i; 281 282 EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 283 if (!(events & (EV_READ|EV_WRITE))) 284 return (0); 285 286 poll_check_ok(pop); 287 i = idx->idxplus1 - 1; 288 if (i < 0) 289 return (-1); 290 291 /* Do we still want to read or write? */ 292 pfd = &pop->event_set[i]; 293 if (events & EV_READ) 294 pfd->events &= ~POLLIN; 295 if (events & EV_WRITE) 296 pfd->events &= ~POLLOUT; 297 poll_check_ok(pop); 298 if (pfd->events) 299 /* Another event cares about that fd. */ 300 return (0); 301 302 /* Okay, so we aren't interested in that fd anymore. */ 303 idx->idxplus1 = 0; 304 305 --pop->nfds; 306 if (i != pop->nfds) { 307 /* 308 * Shift the last pollfd down into the now-unoccupied 309 * position. 310 */ 311 memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], 312 sizeof(struct pollfd)); 313 idx = evmap_io_get_fdinfo(&base->io, pop->event_set[i].fd); 314 EVUTIL_ASSERT(idx); 315 EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); 316 idx->idxplus1 = i + 1; 317 } 318 319 poll_check_ok(pop); 320 return (0); 321 } 322 323 static void 324 poll_dealloc(struct event_base *base) 325 { 326 struct pollop *pop = base->evbase; 327 328 evsig_dealloc(base); 329 if (pop->event_set) 330 mm_free(pop->event_set); 331 if (pop->event_set_copy) 332 mm_free(pop->event_set_copy); 333 334 memset(pop, 0, sizeof(struct pollop)); 335 mm_free(pop); 336 } 337