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 56 struct pollidx { 57 int idxplus1; 58 }; 59 60 struct 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 69 static void *poll_init(struct event_base *); 70 static int poll_add(struct event_base *, int, short old, short events, void *idx); 71 static int poll_del(struct event_base *, int, short old, short events, void *idx); 72 static int poll_dispatch(struct event_base *, struct timeval *); 73 static void poll_dealloc(struct event_base *); 74 75 const 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 87 static void * 88 poll_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 103 static void 104 poll_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 124 static int 125 poll_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 216 static int 217 poll_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 277 static int 278 poll_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 326 static void 327 poll_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