1*eabc0478Schristos /* $NetBSD: poll.c,v 1.6 2024/08/18 20:47:21 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ 48585484eSchristos 58585484eSchristos /* 68585484eSchristos * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> 78585484eSchristos * Copyright 2007-2012 Niels Provos and Nick Mathewson 88585484eSchristos * 98585484eSchristos * Redistribution and use in source and binary forms, with or without 108585484eSchristos * modification, are permitted provided that the following conditions 118585484eSchristos * are met: 128585484eSchristos * 1. Redistributions of source code must retain the above copyright 138585484eSchristos * notice, this list of conditions and the following disclaimer. 148585484eSchristos * 2. Redistributions in binary form must reproduce the above copyright 158585484eSchristos * notice, this list of conditions and the following disclaimer in the 168585484eSchristos * documentation and/or other materials provided with the distribution. 178585484eSchristos * 3. The name of the author may not be used to endorse or promote products 188585484eSchristos * derived from this software without specific prior written permission. 198585484eSchristos * 208585484eSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 218585484eSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 228585484eSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 238585484eSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 248585484eSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 258585484eSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 268585484eSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 278585484eSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 288585484eSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 298585484eSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 308585484eSchristos */ 318585484eSchristos #include "event2/event-config.h" 328585484eSchristos #include "evconfig-private.h" 338585484eSchristos 348585484eSchristos #ifdef EVENT__HAVE_POLL 358585484eSchristos 368585484eSchristos #include <sys/types.h> 378585484eSchristos #ifdef EVENT__HAVE_SYS_TIME_H 388585484eSchristos #include <sys/time.h> 398585484eSchristos #endif 408585484eSchristos #include <sys/queue.h> 418585484eSchristos #include <poll.h> 428585484eSchristos #include <signal.h> 438585484eSchristos #include <limits.h> 448585484eSchristos #include <stdio.h> 458585484eSchristos #include <stdlib.h> 468585484eSchristos #include <string.h> 478585484eSchristos #include <unistd.h> 488585484eSchristos #include <errno.h> 498585484eSchristos 508585484eSchristos #include "event-internal.h" 518585484eSchristos #include "evsignal-internal.h" 528585484eSchristos #include "log-internal.h" 538585484eSchristos #include "evmap-internal.h" 548585484eSchristos #include "event2/thread.h" 558585484eSchristos #include "evthread-internal.h" 568585484eSchristos #include "time-internal.h" 578585484eSchristos 58*eabc0478Schristos /* Since Linux 2.6.17, poll is able to report about peer half-closed connection 59*eabc0478Schristos using special POLLRDHUP flag on a read event. 60*eabc0478Schristos */ 61*eabc0478Schristos #if !defined(POLLRDHUP) 62*eabc0478Schristos #define POLLRDHUP 0 63*eabc0478Schristos #define EARLY_CLOSE_IF_HAVE_RDHUP 0 64*eabc0478Schristos #else 65*eabc0478Schristos #define EARLY_CLOSE_IF_HAVE_RDHUP EV_FEATURE_EARLY_CLOSE 66*eabc0478Schristos #endif 67*eabc0478Schristos 68*eabc0478Schristos 698585484eSchristos struct pollidx { 708585484eSchristos int idxplus1; 718585484eSchristos }; 728585484eSchristos 738585484eSchristos struct pollop { 748585484eSchristos int event_count; /* Highest number alloc */ 758585484eSchristos int nfds; /* Highest number used */ 768585484eSchristos int realloc_copy; /* True iff we must realloc 778585484eSchristos * event_set_copy */ 788585484eSchristos struct pollfd *event_set; 798585484eSchristos struct pollfd *event_set_copy; 808585484eSchristos }; 818585484eSchristos 828585484eSchristos static void *poll_init(struct event_base *); 838585484eSchristos static int poll_add(struct event_base *, int, short old, short events, void *idx); 848585484eSchristos static int poll_del(struct event_base *, int, short old, short events, void *idx); 858585484eSchristos static int poll_dispatch(struct event_base *, struct timeval *); 868585484eSchristos static void poll_dealloc(struct event_base *); 878585484eSchristos 888585484eSchristos const struct eventop pollops = { 898585484eSchristos "poll", 908585484eSchristos poll_init, 918585484eSchristos poll_add, 928585484eSchristos poll_del, 938585484eSchristos poll_dispatch, 948585484eSchristos poll_dealloc, 95*eabc0478Schristos 1, /* need_reinit */ 96*eabc0478Schristos EV_FEATURE_FDS|EARLY_CLOSE_IF_HAVE_RDHUP, 978585484eSchristos sizeof(struct pollidx), 988585484eSchristos }; 998585484eSchristos 1008585484eSchristos static void * 1018585484eSchristos poll_init(struct event_base *base) 1028585484eSchristos { 1038585484eSchristos struct pollop *pollop; 1048585484eSchristos 1058585484eSchristos if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) 1068585484eSchristos return (NULL); 1078585484eSchristos 1088585484eSchristos evsig_init_(base); 1098585484eSchristos 1108585484eSchristos evutil_weakrand_seed_(&base->weakrand_seed, 0); 1118585484eSchristos 1128585484eSchristos return (pollop); 1138585484eSchristos } 1148585484eSchristos 1158585484eSchristos #ifdef CHECK_INVARIANTS 1168585484eSchristos static void 1178585484eSchristos poll_check_ok(struct pollop *pop) 1188585484eSchristos { 1198585484eSchristos int i, idx; 1208585484eSchristos struct event *ev; 1218585484eSchristos 1228585484eSchristos for (i = 0; i < pop->fd_count; ++i) { 1238585484eSchristos idx = pop->idxplus1_by_fd[i]-1; 1248585484eSchristos if (idx < 0) 1258585484eSchristos continue; 1268585484eSchristos EVUTIL_ASSERT(pop->event_set[idx].fd == i); 1278585484eSchristos } 1288585484eSchristos for (i = 0; i < pop->nfds; ++i) { 1298585484eSchristos struct pollfd *pfd = &pop->event_set[i]; 1308585484eSchristos EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); 1318585484eSchristos } 1328585484eSchristos } 1338585484eSchristos #else 1348585484eSchristos #define poll_check_ok(pop) 1358585484eSchristos #endif 1368585484eSchristos 1378585484eSchristos static int 1388585484eSchristos poll_dispatch(struct event_base *base, struct timeval *tv) 1398585484eSchristos { 1408585484eSchristos int res, i, j, nfds; 1418585484eSchristos long msec = -1; 1428585484eSchristos struct pollop *pop = base->evbase; 1438585484eSchristos struct pollfd *event_set; 1448585484eSchristos 1458585484eSchristos poll_check_ok(pop); 1468585484eSchristos 1478585484eSchristos nfds = pop->nfds; 1488585484eSchristos 1498585484eSchristos #ifndef EVENT__DISABLE_THREAD_SUPPORT 1508585484eSchristos if (base->th_base_lock) { 1518585484eSchristos /* If we're using this backend in a multithreaded setting, 1528585484eSchristos * then we need to work on a copy of event_set, so that we can 1538585484eSchristos * let other threads modify the main event_set while we're 1548585484eSchristos * polling. If we're not multithreaded, then we'll skip the 1558585484eSchristos * copy step here to save memory and time. */ 1568585484eSchristos if (pop->realloc_copy) { 1578585484eSchristos struct pollfd *tmp = mm_realloc(pop->event_set_copy, 1588585484eSchristos pop->event_count * sizeof(struct pollfd)); 1598585484eSchristos if (tmp == NULL) { 1608585484eSchristos event_warn("realloc"); 1618585484eSchristos return -1; 1628585484eSchristos } 1638585484eSchristos pop->event_set_copy = tmp; 1648585484eSchristos pop->realloc_copy = 0; 1658585484eSchristos } 1668585484eSchristos memcpy(pop->event_set_copy, pop->event_set, 1678585484eSchristos sizeof(struct pollfd)*nfds); 1688585484eSchristos event_set = pop->event_set_copy; 1698585484eSchristos } else { 1708585484eSchristos event_set = pop->event_set; 1718585484eSchristos } 1728585484eSchristos #else 1738585484eSchristos event_set = pop->event_set; 1748585484eSchristos #endif 1758585484eSchristos 1768585484eSchristos if (tv != NULL) { 1778585484eSchristos msec = evutil_tv_to_msec_(tv); 1788585484eSchristos if (msec < 0 || msec > INT_MAX) 1798585484eSchristos msec = INT_MAX; 1808585484eSchristos } 1818585484eSchristos 1828585484eSchristos EVBASE_RELEASE_LOCK(base, th_base_lock); 1838585484eSchristos 1848585484eSchristos res = poll(event_set, nfds, msec); 1858585484eSchristos 1868585484eSchristos EVBASE_ACQUIRE_LOCK(base, th_base_lock); 1878585484eSchristos 1888585484eSchristos if (res == -1) { 1898585484eSchristos if (errno != EINTR) { 1908585484eSchristos event_warn("poll"); 1918585484eSchristos return (-1); 1928585484eSchristos } 1938585484eSchristos 1948585484eSchristos return (0); 1958585484eSchristos } 1968585484eSchristos 1978585484eSchristos event_debug(("%s: poll reports %d", __func__, res)); 1988585484eSchristos 1998585484eSchristos if (res == 0 || nfds == 0) 2008585484eSchristos return (0); 2018585484eSchristos 2028585484eSchristos i = evutil_weakrand_range_(&base->weakrand_seed, nfds); 2038585484eSchristos for (j = 0; j < nfds; j++) { 2048585484eSchristos int what; 2058585484eSchristos if (++i == nfds) 2068585484eSchristos i = 0; 2078585484eSchristos what = event_set[i].revents; 2088585484eSchristos if (!what) 2098585484eSchristos continue; 2108585484eSchristos 2118585484eSchristos res = 0; 2128585484eSchristos 2138585484eSchristos /* If the file gets closed notify */ 214*eabc0478Schristos if (what & (POLLHUP|POLLERR|POLLNVAL)) 2158585484eSchristos what |= POLLIN|POLLOUT; 2168585484eSchristos if (what & POLLIN) 2178585484eSchristos res |= EV_READ; 2188585484eSchristos if (what & POLLOUT) 2198585484eSchristos res |= EV_WRITE; 220*eabc0478Schristos if (what & POLLRDHUP) 221*eabc0478Schristos res |= EV_CLOSED; 2228585484eSchristos if (res == 0) 2238585484eSchristos continue; 2248585484eSchristos 2258585484eSchristos evmap_io_active_(base, event_set[i].fd, res); 2268585484eSchristos } 2278585484eSchristos 2288585484eSchristos return (0); 2298585484eSchristos } 2308585484eSchristos 2318585484eSchristos static int 2328585484eSchristos poll_add(struct event_base *base, int fd, short old, short events, void *idx_) 2338585484eSchristos { 2348585484eSchristos struct pollop *pop = base->evbase; 2358585484eSchristos struct pollfd *pfd = NULL; 2368585484eSchristos struct pollidx *idx = idx_; 2378585484eSchristos int i; 2388585484eSchristos 2398585484eSchristos EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 240*eabc0478Schristos if (!(events & (EV_READ|EV_WRITE|EV_CLOSED))) 2418585484eSchristos return (0); 2428585484eSchristos 2438585484eSchristos poll_check_ok(pop); 2448585484eSchristos if (pop->nfds + 1 >= pop->event_count) { 2458585484eSchristos struct pollfd *tmp_event_set; 2468585484eSchristos int tmp_event_count; 2478585484eSchristos 2488585484eSchristos if (pop->event_count < 32) 2498585484eSchristos tmp_event_count = 32; 2508585484eSchristos else 2518585484eSchristos tmp_event_count = pop->event_count * 2; 2528585484eSchristos 2538585484eSchristos /* We need more file descriptors */ 2548585484eSchristos tmp_event_set = mm_realloc(pop->event_set, 2558585484eSchristos tmp_event_count * sizeof(struct pollfd)); 2568585484eSchristos if (tmp_event_set == NULL) { 2578585484eSchristos event_warn("realloc"); 2588585484eSchristos return (-1); 2598585484eSchristos } 2608585484eSchristos pop->event_set = tmp_event_set; 2618585484eSchristos 2628585484eSchristos pop->event_count = tmp_event_count; 2638585484eSchristos pop->realloc_copy = 1; 2648585484eSchristos } 2658585484eSchristos 2668585484eSchristos i = idx->idxplus1 - 1; 2678585484eSchristos 2688585484eSchristos if (i >= 0) { 2698585484eSchristos pfd = &pop->event_set[i]; 2708585484eSchristos } else { 2718585484eSchristos i = pop->nfds++; 2728585484eSchristos pfd = &pop->event_set[i]; 2738585484eSchristos pfd->events = 0; 2748585484eSchristos pfd->fd = fd; 2758585484eSchristos idx->idxplus1 = i + 1; 2768585484eSchristos } 2778585484eSchristos 2788585484eSchristos pfd->revents = 0; 2798585484eSchristos if (events & EV_WRITE) 2808585484eSchristos pfd->events |= POLLOUT; 2818585484eSchristos if (events & EV_READ) 2828585484eSchristos pfd->events |= POLLIN; 283*eabc0478Schristos if (events & EV_CLOSED) 284*eabc0478Schristos pfd->events |= POLLRDHUP; 2858585484eSchristos poll_check_ok(pop); 2868585484eSchristos 2878585484eSchristos return (0); 2888585484eSchristos } 2898585484eSchristos 2908585484eSchristos /* 2918585484eSchristos * Nothing to be done here. 2928585484eSchristos */ 2938585484eSchristos 2948585484eSchristos static int 2958585484eSchristos poll_del(struct event_base *base, int fd, short old, short events, void *idx_) 2968585484eSchristos { 2978585484eSchristos struct pollop *pop = base->evbase; 2988585484eSchristos struct pollfd *pfd = NULL; 2998585484eSchristos struct pollidx *idx = idx_; 3008585484eSchristos int i; 3018585484eSchristos 3028585484eSchristos EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 303*eabc0478Schristos if (!(events & (EV_READ|EV_WRITE|EV_CLOSED))) 3048585484eSchristos return (0); 3058585484eSchristos 3068585484eSchristos poll_check_ok(pop); 3078585484eSchristos i = idx->idxplus1 - 1; 3088585484eSchristos if (i < 0) 3098585484eSchristos return (-1); 3108585484eSchristos 3118585484eSchristos /* Do we still want to read or write? */ 3128585484eSchristos pfd = &pop->event_set[i]; 3138585484eSchristos if (events & EV_READ) 3148585484eSchristos pfd->events &= ~POLLIN; 3158585484eSchristos if (events & EV_WRITE) 3168585484eSchristos pfd->events &= ~POLLOUT; 317*eabc0478Schristos if (events & EV_CLOSED) 318*eabc0478Schristos pfd->events &= ~POLLRDHUP; 3198585484eSchristos poll_check_ok(pop); 3208585484eSchristos if (pfd->events) 3218585484eSchristos /* Another event cares about that fd. */ 3228585484eSchristos return (0); 3238585484eSchristos 3248585484eSchristos /* Okay, so we aren't interested in that fd anymore. */ 3258585484eSchristos idx->idxplus1 = 0; 3268585484eSchristos 3278585484eSchristos --pop->nfds; 3288585484eSchristos if (i != pop->nfds) { 3298585484eSchristos /* 3308585484eSchristos * Shift the last pollfd down into the now-unoccupied 3318585484eSchristos * position. 3328585484eSchristos */ 3338585484eSchristos memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], 3348585484eSchristos sizeof(struct pollfd)); 3358585484eSchristos idx = evmap_io_get_fdinfo_(&base->io, pop->event_set[i].fd); 3368585484eSchristos EVUTIL_ASSERT(idx); 3378585484eSchristos EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); 3388585484eSchristos idx->idxplus1 = i + 1; 3398585484eSchristos } 3408585484eSchristos 3418585484eSchristos poll_check_ok(pop); 3428585484eSchristos return (0); 3438585484eSchristos } 3448585484eSchristos 3458585484eSchristos static void 3468585484eSchristos poll_dealloc(struct event_base *base) 3478585484eSchristos { 3488585484eSchristos struct pollop *pop = base->evbase; 3498585484eSchristos 3508585484eSchristos evsig_dealloc_(base); 3518585484eSchristos if (pop->event_set) 3528585484eSchristos mm_free(pop->event_set); 3538585484eSchristos if (pop->event_set_copy) 3548585484eSchristos mm_free(pop->event_set_copy); 3558585484eSchristos 3568585484eSchristos memset(pop, 0, sizeof(struct pollop)); 3578585484eSchristos mm_free(pop); 3588585484eSchristos } 3598585484eSchristos 3608585484eSchristos #endif /* EVENT__HAVE_POLL */ 361