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