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