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