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