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