1*657871a7Schristos /* $NetBSD: poll.c,v 1.1.1.4 2021/04/07 02:43:13 christos Exp $ */
26448a78cSplunky /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
36448a78cSplunky
46448a78cSplunky /*
56ecf6635Schristos * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
66ecf6635Schristos * Copyright 2007-2012 Niels Provos and Nick Mathewson
76448a78cSplunky *
86448a78cSplunky * Redistribution and use in source and binary forms, with or without
96448a78cSplunky * modification, are permitted provided that the following conditions
106448a78cSplunky * are met:
116448a78cSplunky * 1. Redistributions of source code must retain the above copyright
126448a78cSplunky * notice, this list of conditions and the following disclaimer.
136448a78cSplunky * 2. Redistributions in binary form must reproduce the above copyright
146448a78cSplunky * notice, this list of conditions and the following disclaimer in the
156448a78cSplunky * documentation and/or other materials provided with the distribution.
166448a78cSplunky * 3. The name of the author may not be used to endorse or promote products
176448a78cSplunky * derived from this software without specific prior written permission.
186448a78cSplunky *
196448a78cSplunky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
206448a78cSplunky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
216448a78cSplunky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
226448a78cSplunky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
236448a78cSplunky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
246448a78cSplunky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
256448a78cSplunky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
266448a78cSplunky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
276448a78cSplunky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
286448a78cSplunky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
296448a78cSplunky */
306ecf6635Schristos #include "event2/event-config.h"
316ecf6635Schristos #include <sys/cdefs.h>
32*657871a7Schristos __RCSID("$NetBSD: poll.c,v 1.1.1.4 2021/04/07 02:43:13 christos Exp $");
33805a1ce9Schristos #include "evconfig-private.h"
34805a1ce9Schristos
35805a1ce9Schristos #ifdef EVENT__HAVE_POLL
366448a78cSplunky
376448a78cSplunky #include <sys/types.h>
38805a1ce9Schristos #ifdef EVENT__HAVE_SYS_TIME_H
396448a78cSplunky #include <sys/time.h>
406448a78cSplunky #endif
416448a78cSplunky #include <sys/queue.h>
426448a78cSplunky #include <poll.h>
436448a78cSplunky #include <signal.h>
446ecf6635Schristos #include <limits.h>
456448a78cSplunky #include <stdio.h>
466448a78cSplunky #include <stdlib.h>
476448a78cSplunky #include <string.h>
486448a78cSplunky #include <unistd.h>
496448a78cSplunky #include <errno.h>
506448a78cSplunky
516448a78cSplunky #include "event-internal.h"
526ecf6635Schristos #include "evsignal-internal.h"
536ecf6635Schristos #include "log-internal.h"
546ecf6635Schristos #include "evmap-internal.h"
556ecf6635Schristos #include "event2/thread.h"
566ecf6635Schristos #include "evthread-internal.h"
57805a1ce9Schristos #include "time-internal.h"
586ecf6635Schristos
59*657871a7Schristos /* Since Linux 2.6.17, poll is able to report about peer half-closed connection
60*657871a7Schristos using special POLLRDHUP flag on a read event.
61*657871a7Schristos */
62*657871a7Schristos #if !defined(POLLRDHUP)
63*657871a7Schristos #define POLLRDHUP 0
64*657871a7Schristos #define EARLY_CLOSE_IF_HAVE_RDHUP 0
65*657871a7Schristos #else
66*657871a7Schristos #define EARLY_CLOSE_IF_HAVE_RDHUP EV_FEATURE_EARLY_CLOSE
67*657871a7Schristos #endif
68*657871a7Schristos
69*657871a7Schristos
706ecf6635Schristos struct pollidx {
716ecf6635Schristos int idxplus1;
726ecf6635Schristos };
736448a78cSplunky
746448a78cSplunky struct pollop {
756448a78cSplunky int event_count; /* Highest number alloc */
766ecf6635Schristos int nfds; /* Highest number used */
776ecf6635Schristos int realloc_copy; /* True iff we must realloc
786ecf6635Schristos * event_set_copy */
796448a78cSplunky struct pollfd *event_set;
806ecf6635Schristos struct pollfd *event_set_copy;
816448a78cSplunky };
826448a78cSplunky
836448a78cSplunky static void *poll_init(struct event_base *);
84805a1ce9Schristos static int poll_add(struct event_base *, int, short old, short events, void *idx);
85805a1ce9Schristos static int poll_del(struct event_base *, int, short old, short events, void *idx);
866ecf6635Schristos static int poll_dispatch(struct event_base *, struct timeval *);
876ecf6635Schristos static void poll_dealloc(struct event_base *);
886448a78cSplunky
896448a78cSplunky const struct eventop pollops = {
906448a78cSplunky "poll",
916448a78cSplunky poll_init,
926448a78cSplunky poll_add,
936448a78cSplunky poll_del,
946448a78cSplunky poll_dispatch,
956448a78cSplunky poll_dealloc,
96*657871a7Schristos 1, /* need_reinit */
97*657871a7Schristos EV_FEATURE_FDS|EARLY_CLOSE_IF_HAVE_RDHUP,
986ecf6635Schristos sizeof(struct pollidx),
996448a78cSplunky };
1006448a78cSplunky
1016448a78cSplunky static void *
poll_init(struct event_base * base)1026448a78cSplunky poll_init(struct event_base *base)
1036448a78cSplunky {
1046448a78cSplunky struct pollop *pollop;
1056448a78cSplunky
1066ecf6635Schristos if (!(pollop = mm_calloc(1, sizeof(struct pollop))))
1076448a78cSplunky return (NULL);
1086448a78cSplunky
109805a1ce9Schristos evsig_init_(base);
110805a1ce9Schristos
111805a1ce9Schristos evutil_weakrand_seed_(&base->weakrand_seed, 0);
1126448a78cSplunky
1136448a78cSplunky return (pollop);
1146448a78cSplunky }
1156448a78cSplunky
1166448a78cSplunky #ifdef CHECK_INVARIANTS
1176448a78cSplunky static void
poll_check_ok(struct pollop * pop)1186448a78cSplunky poll_check_ok(struct pollop *pop)
1196448a78cSplunky {
1206448a78cSplunky int i, idx;
1216448a78cSplunky struct event *ev;
1226448a78cSplunky
1236448a78cSplunky for (i = 0; i < pop->fd_count; ++i) {
1246448a78cSplunky idx = pop->idxplus1_by_fd[i]-1;
1256448a78cSplunky if (idx < 0)
1266448a78cSplunky continue;
1276ecf6635Schristos EVUTIL_ASSERT(pop->event_set[idx].fd == i);
1286448a78cSplunky }
1296448a78cSplunky for (i = 0; i < pop->nfds; ++i) {
1306448a78cSplunky struct pollfd *pfd = &pop->event_set[i];
1316ecf6635Schristos EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1);
1326448a78cSplunky }
1336448a78cSplunky }
1346448a78cSplunky #else
1356448a78cSplunky #define poll_check_ok(pop)
1366448a78cSplunky #endif
1376448a78cSplunky
1386448a78cSplunky static int
poll_dispatch(struct event_base * base,struct timeval * tv)1396ecf6635Schristos poll_dispatch(struct event_base *base, struct timeval *tv)
1406448a78cSplunky {
1416ecf6635Schristos int res, i, j, nfds;
1426ecf6635Schristos long msec = -1;
1436ecf6635Schristos struct pollop *pop = base->evbase;
1446ecf6635Schristos struct pollfd *event_set;
1456448a78cSplunky
1466448a78cSplunky poll_check_ok(pop);
1476448a78cSplunky
1486448a78cSplunky nfds = pop->nfds;
1496ecf6635Schristos
150805a1ce9Schristos #ifndef EVENT__DISABLE_THREAD_SUPPORT
1516ecf6635Schristos if (base->th_base_lock) {
1526ecf6635Schristos /* If we're using this backend in a multithreaded setting,
1536ecf6635Schristos * then we need to work on a copy of event_set, so that we can
1546ecf6635Schristos * let other threads modify the main event_set while we're
1556ecf6635Schristos * polling. If we're not multithreaded, then we'll skip the
1566ecf6635Schristos * copy step here to save memory and time. */
1576ecf6635Schristos if (pop->realloc_copy) {
1586ecf6635Schristos struct pollfd *tmp = mm_realloc(pop->event_set_copy,
1596ecf6635Schristos pop->event_count * sizeof(struct pollfd));
1606ecf6635Schristos if (tmp == NULL) {
1616ecf6635Schristos event_warn("realloc");
1626ecf6635Schristos return -1;
1636ecf6635Schristos }
1646ecf6635Schristos pop->event_set_copy = tmp;
1656ecf6635Schristos pop->realloc_copy = 0;
1666ecf6635Schristos }
1676ecf6635Schristos memcpy(pop->event_set_copy, pop->event_set,
1686ecf6635Schristos sizeof(struct pollfd)*nfds);
1696ecf6635Schristos event_set = pop->event_set_copy;
1706ecf6635Schristos } else {
1716ecf6635Schristos event_set = pop->event_set;
1726ecf6635Schristos }
1736ecf6635Schristos #else
1746ecf6635Schristos event_set = pop->event_set;
1756ecf6635Schristos #endif
1766ecf6635Schristos
1776ecf6635Schristos if (tv != NULL) {
178805a1ce9Schristos msec = evutil_tv_to_msec_(tv);
1796ecf6635Schristos if (msec < 0 || msec > INT_MAX)
1806ecf6635Schristos msec = INT_MAX;
1816ecf6635Schristos }
1826ecf6635Schristos
1836ecf6635Schristos EVBASE_RELEASE_LOCK(base, th_base_lock);
1846ecf6635Schristos
1856ecf6635Schristos res = poll(event_set, nfds, msec);
1866ecf6635Schristos
1876ecf6635Schristos EVBASE_ACQUIRE_LOCK(base, th_base_lock);
1886448a78cSplunky
1896448a78cSplunky if (res == -1) {
1906448a78cSplunky if (errno != EINTR) {
1916448a78cSplunky event_warn("poll");
1926448a78cSplunky return (-1);
1936448a78cSplunky }
1946448a78cSplunky
1956448a78cSplunky return (0);
1966448a78cSplunky }
1976448a78cSplunky
1986448a78cSplunky event_debug(("%s: poll reports %d", __func__, res));
1996448a78cSplunky
2006448a78cSplunky if (res == 0 || nfds == 0)
2016448a78cSplunky return (0);
2026448a78cSplunky
203805a1ce9Schristos i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
2046448a78cSplunky for (j = 0; j < nfds; j++) {
2056448a78cSplunky int what;
2066448a78cSplunky if (++i == nfds)
2076448a78cSplunky i = 0;
2086ecf6635Schristos what = event_set[i].revents;
2096448a78cSplunky if (!what)
2106448a78cSplunky continue;
2116448a78cSplunky
2126448a78cSplunky res = 0;
2136448a78cSplunky
2146448a78cSplunky /* If the file gets closed notify */
215805a1ce9Schristos if (what & (POLLHUP|POLLERR|POLLNVAL))
2166448a78cSplunky what |= POLLIN|POLLOUT;
2176ecf6635Schristos if (what & POLLIN)
2186448a78cSplunky res |= EV_READ;
2196ecf6635Schristos if (what & POLLOUT)
2206448a78cSplunky res |= EV_WRITE;
221*657871a7Schristos if (what & POLLRDHUP)
222*657871a7Schristos res |= EV_CLOSED;
2236448a78cSplunky if (res == 0)
2246448a78cSplunky continue;
2256448a78cSplunky
226805a1ce9Schristos evmap_io_active_(base, event_set[i].fd, res);
2276448a78cSplunky }
2286448a78cSplunky
2296448a78cSplunky return (0);
2306448a78cSplunky }
2316448a78cSplunky
2326448a78cSplunky static int
poll_add(struct event_base * base,int fd,short old,short events,void * idx_)233805a1ce9Schristos poll_add(struct event_base *base, int fd, short old, short events, void *idx_)
2346448a78cSplunky {
2356ecf6635Schristos struct pollop *pop = base->evbase;
2366448a78cSplunky struct pollfd *pfd = NULL;
237805a1ce9Schristos struct pollidx *idx = idx_;
2386448a78cSplunky int i;
2396448a78cSplunky
2406ecf6635Schristos EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
241*657871a7Schristos if (!(events & (EV_READ|EV_WRITE|EV_CLOSED)))
2426448a78cSplunky return (0);
2436448a78cSplunky
2446448a78cSplunky poll_check_ok(pop);
2456448a78cSplunky if (pop->nfds + 1 >= pop->event_count) {
2466448a78cSplunky struct pollfd *tmp_event_set;
2476448a78cSplunky int tmp_event_count;
2486448a78cSplunky
2496448a78cSplunky if (pop->event_count < 32)
2506448a78cSplunky tmp_event_count = 32;
2516448a78cSplunky else
2526448a78cSplunky tmp_event_count = pop->event_count * 2;
2536448a78cSplunky
2546448a78cSplunky /* We need more file descriptors */
2556ecf6635Schristos tmp_event_set = mm_realloc(pop->event_set,
2566448a78cSplunky tmp_event_count * sizeof(struct pollfd));
2576448a78cSplunky if (tmp_event_set == NULL) {
2586448a78cSplunky event_warn("realloc");
2596448a78cSplunky return (-1);
2606448a78cSplunky }
2616448a78cSplunky pop->event_set = tmp_event_set;
2626448a78cSplunky
2636448a78cSplunky pop->event_count = tmp_event_count;
2646ecf6635Schristos pop->realloc_copy = 1;
2656448a78cSplunky }
2666448a78cSplunky
2676ecf6635Schristos i = idx->idxplus1 - 1;
2686ecf6635Schristos
2696448a78cSplunky if (i >= 0) {
2706448a78cSplunky pfd = &pop->event_set[i];
2716448a78cSplunky } else {
2726448a78cSplunky i = pop->nfds++;
2736448a78cSplunky pfd = &pop->event_set[i];
2746448a78cSplunky pfd->events = 0;
2756ecf6635Schristos pfd->fd = fd;
2766ecf6635Schristos idx->idxplus1 = i + 1;
2776448a78cSplunky }
2786448a78cSplunky
2796448a78cSplunky pfd->revents = 0;
2806ecf6635Schristos if (events & EV_WRITE)
2816448a78cSplunky pfd->events |= POLLOUT;
2826ecf6635Schristos if (events & EV_READ)
2836448a78cSplunky pfd->events |= POLLIN;
284*657871a7Schristos if (events & EV_CLOSED)
285*657871a7Schristos pfd->events |= POLLRDHUP;
2866448a78cSplunky poll_check_ok(pop);
2876448a78cSplunky
2886448a78cSplunky return (0);
2896448a78cSplunky }
2906448a78cSplunky
2916448a78cSplunky /*
2926448a78cSplunky * Nothing to be done here.
2936448a78cSplunky */
2946448a78cSplunky
2956448a78cSplunky static int
poll_del(struct event_base * base,int fd,short old,short events,void * idx_)296805a1ce9Schristos poll_del(struct event_base *base, int fd, short old, short events, void *idx_)
2976448a78cSplunky {
2986ecf6635Schristos struct pollop *pop = base->evbase;
2996448a78cSplunky struct pollfd *pfd = NULL;
300805a1ce9Schristos struct pollidx *idx = idx_;
3016448a78cSplunky int i;
3026448a78cSplunky
3036ecf6635Schristos EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
304*657871a7Schristos if (!(events & (EV_READ|EV_WRITE|EV_CLOSED)))
3056448a78cSplunky return (0);
3066448a78cSplunky
3076448a78cSplunky poll_check_ok(pop);
3086ecf6635Schristos i = idx->idxplus1 - 1;
3096448a78cSplunky if (i < 0)
3106448a78cSplunky return (-1);
3116448a78cSplunky
3126448a78cSplunky /* Do we still want to read or write? */
3136448a78cSplunky pfd = &pop->event_set[i];
3146ecf6635Schristos if (events & EV_READ)
3156448a78cSplunky pfd->events &= ~POLLIN;
3166ecf6635Schristos if (events & EV_WRITE)
3176448a78cSplunky pfd->events &= ~POLLOUT;
318*657871a7Schristos if (events & EV_CLOSED)
319*657871a7Schristos pfd->events &= ~POLLRDHUP;
3206448a78cSplunky poll_check_ok(pop);
3216448a78cSplunky if (pfd->events)
3226448a78cSplunky /* Another event cares about that fd. */
3236448a78cSplunky return (0);
3246448a78cSplunky
3256448a78cSplunky /* Okay, so we aren't interested in that fd anymore. */
3266ecf6635Schristos idx->idxplus1 = 0;
3276448a78cSplunky
3286448a78cSplunky --pop->nfds;
3296448a78cSplunky if (i != pop->nfds) {
3306448a78cSplunky /*
3316448a78cSplunky * Shift the last pollfd down into the now-unoccupied
3326448a78cSplunky * position.
3336448a78cSplunky */
3346448a78cSplunky memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
3356448a78cSplunky sizeof(struct pollfd));
336805a1ce9Schristos idx = evmap_io_get_fdinfo_(&base->io, pop->event_set[i].fd);
3376ecf6635Schristos EVUTIL_ASSERT(idx);
3386ecf6635Schristos EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1);
3396ecf6635Schristos idx->idxplus1 = i + 1;
3406448a78cSplunky }
3416448a78cSplunky
3426448a78cSplunky poll_check_ok(pop);
3436448a78cSplunky return (0);
3446448a78cSplunky }
3456448a78cSplunky
3466448a78cSplunky static void
poll_dealloc(struct event_base * base)3476ecf6635Schristos poll_dealloc(struct event_base *base)
3486448a78cSplunky {
3496ecf6635Schristos struct pollop *pop = base->evbase;
3506448a78cSplunky
351805a1ce9Schristos evsig_dealloc_(base);
3526448a78cSplunky if (pop->event_set)
3536ecf6635Schristos mm_free(pop->event_set);
3546ecf6635Schristos if (pop->event_set_copy)
3556ecf6635Schristos mm_free(pop->event_set_copy);
3566448a78cSplunky
3576448a78cSplunky memset(pop, 0, sizeof(struct pollop));
3586ecf6635Schristos mm_free(pop);
3596448a78cSplunky }
360805a1ce9Schristos
361805a1ce9Schristos #endif /* EVENT__HAVE_POLL */
362