1 1.5 christos /* $NetBSD: poll.c,v 1.6 2024/08/18 20:47:21 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ 4 1.1 christos 5 1.1 christos /* 6 1.1 christos * Copyright 2000-2007 Niels Provos <provos (at) citi.umich.edu> 7 1.1 christos * Copyright 2007-2012 Niels Provos and Nick Mathewson 8 1.1 christos * 9 1.1 christos * Redistribution and use in source and binary forms, with or without 10 1.1 christos * modification, are permitted provided that the following conditions 11 1.1 christos * are met: 12 1.1 christos * 1. Redistributions of source code must retain the above copyright 13 1.1 christos * notice, this list of conditions and the following disclaimer. 14 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 christos * notice, this list of conditions and the following disclaimer in the 16 1.1 christos * documentation and/or other materials provided with the distribution. 17 1.1 christos * 3. The name of the author may not be used to endorse or promote products 18 1.1 christos * derived from this software without specific prior written permission. 19 1.1 christos * 20 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 1.1 christos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 1.1 christos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 1.1 christos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 1.1 christos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 1.1 christos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 1.1 christos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 1.1 christos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 1.1 christos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 1.1 christos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 1.1 christos */ 31 1.1 christos #include "event2/event-config.h" 32 1.1 christos #include "evconfig-private.h" 33 1.1 christos 34 1.1 christos #ifdef EVENT__HAVE_POLL 35 1.1 christos 36 1.1 christos #include <sys/types.h> 37 1.1 christos #ifdef EVENT__HAVE_SYS_TIME_H 38 1.1 christos #include <sys/time.h> 39 1.1 christos #endif 40 1.1 christos #include <sys/queue.h> 41 1.1 christos #include <poll.h> 42 1.1 christos #include <signal.h> 43 1.1 christos #include <limits.h> 44 1.1 christos #include <stdio.h> 45 1.1 christos #include <stdlib.h> 46 1.1 christos #include <string.h> 47 1.1 christos #include <unistd.h> 48 1.1 christos #include <errno.h> 49 1.1 christos 50 1.1 christos #include "event-internal.h" 51 1.1 christos #include "evsignal-internal.h" 52 1.1 christos #include "log-internal.h" 53 1.1 christos #include "evmap-internal.h" 54 1.1 christos #include "event2/thread.h" 55 1.1 christos #include "evthread-internal.h" 56 1.1 christos #include "time-internal.h" 57 1.1 christos 58 1.6 christos /* Since Linux 2.6.17, poll is able to report about peer half-closed connection 59 1.6 christos using special POLLRDHUP flag on a read event. 60 1.6 christos */ 61 1.6 christos #if !defined(POLLRDHUP) 62 1.6 christos #define POLLRDHUP 0 63 1.6 christos #define EARLY_CLOSE_IF_HAVE_RDHUP 0 64 1.6 christos #else 65 1.6 christos #define EARLY_CLOSE_IF_HAVE_RDHUP EV_FEATURE_EARLY_CLOSE 66 1.6 christos #endif 67 1.6 christos 68 1.6 christos 69 1.1 christos struct pollidx { 70 1.1 christos int idxplus1; 71 1.1 christos }; 72 1.1 christos 73 1.1 christos struct pollop { 74 1.1 christos int event_count; /* Highest number alloc */ 75 1.1 christos int nfds; /* Highest number used */ 76 1.1 christos int realloc_copy; /* True iff we must realloc 77 1.1 christos * event_set_copy */ 78 1.1 christos struct pollfd *event_set; 79 1.1 christos struct pollfd *event_set_copy; 80 1.1 christos }; 81 1.1 christos 82 1.1 christos static void *poll_init(struct event_base *); 83 1.1 christos static int poll_add(struct event_base *, int, short old, short events, void *idx); 84 1.1 christos static int poll_del(struct event_base *, int, short old, short events, void *idx); 85 1.1 christos static int poll_dispatch(struct event_base *, struct timeval *); 86 1.1 christos static void poll_dealloc(struct event_base *); 87 1.1 christos 88 1.1 christos const struct eventop pollops = { 89 1.1 christos "poll", 90 1.1 christos poll_init, 91 1.1 christos poll_add, 92 1.1 christos poll_del, 93 1.1 christos poll_dispatch, 94 1.1 christos poll_dealloc, 95 1.6 christos 1, /* need_reinit */ 96 1.6 christos EV_FEATURE_FDS|EARLY_CLOSE_IF_HAVE_RDHUP, 97 1.1 christos sizeof(struct pollidx), 98 1.1 christos }; 99 1.1 christos 100 1.1 christos static void * 101 1.1 christos poll_init(struct event_base *base) 102 1.1 christos { 103 1.1 christos struct pollop *pollop; 104 1.1 christos 105 1.1 christos if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) 106 1.1 christos return (NULL); 107 1.1 christos 108 1.1 christos evsig_init_(base); 109 1.1 christos 110 1.1 christos evutil_weakrand_seed_(&base->weakrand_seed, 0); 111 1.1 christos 112 1.1 christos return (pollop); 113 1.1 christos } 114 1.1 christos 115 1.1 christos #ifdef CHECK_INVARIANTS 116 1.1 christos static void 117 1.1 christos poll_check_ok(struct pollop *pop) 118 1.1 christos { 119 1.1 christos int i, idx; 120 1.1 christos struct event *ev; 121 1.1 christos 122 1.1 christos for (i = 0; i < pop->fd_count; ++i) { 123 1.1 christos idx = pop->idxplus1_by_fd[i]-1; 124 1.1 christos if (idx < 0) 125 1.1 christos continue; 126 1.1 christos EVUTIL_ASSERT(pop->event_set[idx].fd == i); 127 1.1 christos } 128 1.1 christos for (i = 0; i < pop->nfds; ++i) { 129 1.1 christos struct pollfd *pfd = &pop->event_set[i]; 130 1.1 christos EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); 131 1.1 christos } 132 1.1 christos } 133 1.1 christos #else 134 1.1 christos #define poll_check_ok(pop) 135 1.1 christos #endif 136 1.1 christos 137 1.1 christos static int 138 1.1 christos poll_dispatch(struct event_base *base, struct timeval *tv) 139 1.1 christos { 140 1.1 christos int res, i, j, nfds; 141 1.1 christos long msec = -1; 142 1.1 christos struct pollop *pop = base->evbase; 143 1.1 christos struct pollfd *event_set; 144 1.1 christos 145 1.1 christos poll_check_ok(pop); 146 1.1 christos 147 1.1 christos nfds = pop->nfds; 148 1.1 christos 149 1.1 christos #ifndef EVENT__DISABLE_THREAD_SUPPORT 150 1.1 christos if (base->th_base_lock) { 151 1.1 christos /* If we're using this backend in a multithreaded setting, 152 1.1 christos * then we need to work on a copy of event_set, so that we can 153 1.1 christos * let other threads modify the main event_set while we're 154 1.1 christos * polling. If we're not multithreaded, then we'll skip the 155 1.1 christos * copy step here to save memory and time. */ 156 1.1 christos if (pop->realloc_copy) { 157 1.1 christos struct pollfd *tmp = mm_realloc(pop->event_set_copy, 158 1.1 christos pop->event_count * sizeof(struct pollfd)); 159 1.1 christos if (tmp == NULL) { 160 1.1 christos event_warn("realloc"); 161 1.1 christos return -1; 162 1.1 christos } 163 1.1 christos pop->event_set_copy = tmp; 164 1.1 christos pop->realloc_copy = 0; 165 1.1 christos } 166 1.1 christos memcpy(pop->event_set_copy, pop->event_set, 167 1.1 christos sizeof(struct pollfd)*nfds); 168 1.1 christos event_set = pop->event_set_copy; 169 1.1 christos } else { 170 1.1 christos event_set = pop->event_set; 171 1.1 christos } 172 1.1 christos #else 173 1.1 christos event_set = pop->event_set; 174 1.1 christos #endif 175 1.1 christos 176 1.1 christos if (tv != NULL) { 177 1.1 christos msec = evutil_tv_to_msec_(tv); 178 1.1 christos if (msec < 0 || msec > INT_MAX) 179 1.1 christos msec = INT_MAX; 180 1.1 christos } 181 1.1 christos 182 1.1 christos EVBASE_RELEASE_LOCK(base, th_base_lock); 183 1.1 christos 184 1.1 christos res = poll(event_set, nfds, msec); 185 1.1 christos 186 1.1 christos EVBASE_ACQUIRE_LOCK(base, th_base_lock); 187 1.1 christos 188 1.1 christos if (res == -1) { 189 1.1 christos if (errno != EINTR) { 190 1.1 christos event_warn("poll"); 191 1.1 christos return (-1); 192 1.1 christos } 193 1.1 christos 194 1.1 christos return (0); 195 1.1 christos } 196 1.1 christos 197 1.1 christos event_debug(("%s: poll reports %d", __func__, res)); 198 1.1 christos 199 1.1 christos if (res == 0 || nfds == 0) 200 1.1 christos return (0); 201 1.1 christos 202 1.1 christos i = evutil_weakrand_range_(&base->weakrand_seed, nfds); 203 1.1 christos for (j = 0; j < nfds; j++) { 204 1.1 christos int what; 205 1.1 christos if (++i == nfds) 206 1.1 christos i = 0; 207 1.1 christos what = event_set[i].revents; 208 1.1 christos if (!what) 209 1.1 christos continue; 210 1.1 christos 211 1.1 christos res = 0; 212 1.1 christos 213 1.1 christos /* If the file gets closed notify */ 214 1.6 christos if (what & (POLLHUP|POLLERR|POLLNVAL)) 215 1.1 christos what |= POLLIN|POLLOUT; 216 1.1 christos if (what & POLLIN) 217 1.1 christos res |= EV_READ; 218 1.1 christos if (what & POLLOUT) 219 1.1 christos res |= EV_WRITE; 220 1.6 christos if (what & POLLRDHUP) 221 1.6 christos res |= EV_CLOSED; 222 1.1 christos if (res == 0) 223 1.1 christos continue; 224 1.1 christos 225 1.1 christos evmap_io_active_(base, event_set[i].fd, res); 226 1.1 christos } 227 1.1 christos 228 1.1 christos return (0); 229 1.1 christos } 230 1.1 christos 231 1.1 christos static int 232 1.1 christos poll_add(struct event_base *base, int fd, short old, short events, void *idx_) 233 1.1 christos { 234 1.1 christos struct pollop *pop = base->evbase; 235 1.1 christos struct pollfd *pfd = NULL; 236 1.1 christos struct pollidx *idx = idx_; 237 1.1 christos int i; 238 1.1 christos 239 1.1 christos EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 240 1.6 christos if (!(events & (EV_READ|EV_WRITE|EV_CLOSED))) 241 1.1 christos return (0); 242 1.1 christos 243 1.1 christos poll_check_ok(pop); 244 1.1 christos if (pop->nfds + 1 >= pop->event_count) { 245 1.1 christos struct pollfd *tmp_event_set; 246 1.1 christos int tmp_event_count; 247 1.1 christos 248 1.1 christos if (pop->event_count < 32) 249 1.1 christos tmp_event_count = 32; 250 1.1 christos else 251 1.1 christos tmp_event_count = pop->event_count * 2; 252 1.1 christos 253 1.1 christos /* We need more file descriptors */ 254 1.1 christos tmp_event_set = mm_realloc(pop->event_set, 255 1.1 christos tmp_event_count * sizeof(struct pollfd)); 256 1.1 christos if (tmp_event_set == NULL) { 257 1.1 christos event_warn("realloc"); 258 1.1 christos return (-1); 259 1.1 christos } 260 1.1 christos pop->event_set = tmp_event_set; 261 1.1 christos 262 1.1 christos pop->event_count = tmp_event_count; 263 1.1 christos pop->realloc_copy = 1; 264 1.1 christos } 265 1.1 christos 266 1.1 christos i = idx->idxplus1 - 1; 267 1.1 christos 268 1.1 christos if (i >= 0) { 269 1.1 christos pfd = &pop->event_set[i]; 270 1.1 christos } else { 271 1.1 christos i = pop->nfds++; 272 1.1 christos pfd = &pop->event_set[i]; 273 1.1 christos pfd->events = 0; 274 1.1 christos pfd->fd = fd; 275 1.1 christos idx->idxplus1 = i + 1; 276 1.1 christos } 277 1.1 christos 278 1.1 christos pfd->revents = 0; 279 1.1 christos if (events & EV_WRITE) 280 1.1 christos pfd->events |= POLLOUT; 281 1.1 christos if (events & EV_READ) 282 1.1 christos pfd->events |= POLLIN; 283 1.6 christos if (events & EV_CLOSED) 284 1.6 christos pfd->events |= POLLRDHUP; 285 1.1 christos poll_check_ok(pop); 286 1.1 christos 287 1.1 christos return (0); 288 1.1 christos } 289 1.1 christos 290 1.1 christos /* 291 1.1 christos * Nothing to be done here. 292 1.1 christos */ 293 1.1 christos 294 1.1 christos static int 295 1.1 christos poll_del(struct event_base *base, int fd, short old, short events, void *idx_) 296 1.1 christos { 297 1.1 christos struct pollop *pop = base->evbase; 298 1.1 christos struct pollfd *pfd = NULL; 299 1.1 christos struct pollidx *idx = idx_; 300 1.1 christos int i; 301 1.1 christos 302 1.1 christos EVUTIL_ASSERT((events & EV_SIGNAL) == 0); 303 1.6 christos if (!(events & (EV_READ|EV_WRITE|EV_CLOSED))) 304 1.1 christos return (0); 305 1.1 christos 306 1.1 christos poll_check_ok(pop); 307 1.1 christos i = idx->idxplus1 - 1; 308 1.1 christos if (i < 0) 309 1.1 christos return (-1); 310 1.1 christos 311 1.1 christos /* Do we still want to read or write? */ 312 1.1 christos pfd = &pop->event_set[i]; 313 1.1 christos if (events & EV_READ) 314 1.1 christos pfd->events &= ~POLLIN; 315 1.1 christos if (events & EV_WRITE) 316 1.1 christos pfd->events &= ~POLLOUT; 317 1.6 christos if (events & EV_CLOSED) 318 1.6 christos pfd->events &= ~POLLRDHUP; 319 1.1 christos poll_check_ok(pop); 320 1.1 christos if (pfd->events) 321 1.1 christos /* Another event cares about that fd. */ 322 1.1 christos return (0); 323 1.1 christos 324 1.1 christos /* Okay, so we aren't interested in that fd anymore. */ 325 1.1 christos idx->idxplus1 = 0; 326 1.1 christos 327 1.1 christos --pop->nfds; 328 1.1 christos if (i != pop->nfds) { 329 1.1 christos /* 330 1.1 christos * Shift the last pollfd down into the now-unoccupied 331 1.1 christos * position. 332 1.1 christos */ 333 1.1 christos memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], 334 1.1 christos sizeof(struct pollfd)); 335 1.1 christos idx = evmap_io_get_fdinfo_(&base->io, pop->event_set[i].fd); 336 1.1 christos EVUTIL_ASSERT(idx); 337 1.1 christos EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); 338 1.1 christos idx->idxplus1 = i + 1; 339 1.1 christos } 340 1.1 christos 341 1.1 christos poll_check_ok(pop); 342 1.1 christos return (0); 343 1.1 christos } 344 1.1 christos 345 1.1 christos static void 346 1.1 christos poll_dealloc(struct event_base *base) 347 1.1 christos { 348 1.1 christos struct pollop *pop = base->evbase; 349 1.1 christos 350 1.1 christos evsig_dealloc_(base); 351 1.1 christos if (pop->event_set) 352 1.1 christos mm_free(pop->event_set); 353 1.1 christos if (pop->event_set_copy) 354 1.1 christos mm_free(pop->event_set_copy); 355 1.1 christos 356 1.1 christos memset(pop, 0, sizeof(struct pollop)); 357 1.1 christos mm_free(pop); 358 1.1 christos } 359 1.1 christos 360 1.1 christos #endif /* EVENT__HAVE_POLL */ 361