1*81d2345cSrillig /* $NetBSD: select.c,v 1.5 2021/04/10 19:18:45 rillig Exp $ */
26448a78cSplunky /* $OpenBSD: select.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*81d2345cSrillig __RCSID("$NetBSD: select.c,v 1.5 2021/04/10 19:18:45 rillig Exp $");
330d738af4Schristos #include "evconfig-private.h"
340d738af4Schristos
350d738af4Schristos #ifdef EVENT__HAVE_SELECT
360d738af4Schristos
370d738af4Schristos #ifdef __APPLE__
380d738af4Schristos /* Apple wants us to define this if we might ever pass more than
390d738af4Schristos * FD_SETSIZE bits to select(). */
400d738af4Schristos #define _DARWIN_UNLIMITED_SELECT
410d738af4Schristos #endif
426448a78cSplunky
436448a78cSplunky #include <sys/types.h>
440d738af4Schristos #ifdef EVENT__HAVE_SYS_TIME_H
456448a78cSplunky #include <sys/time.h>
466448a78cSplunky #endif
470d738af4Schristos #ifdef EVENT__HAVE_SYS_SELECT_H
486448a78cSplunky #include <sys/select.h>
496448a78cSplunky #endif
506448a78cSplunky #include <sys/queue.h>
516448a78cSplunky #include <signal.h>
526448a78cSplunky #include <stdio.h>
536448a78cSplunky #include <stdlib.h>
546448a78cSplunky #include <string.h>
556448a78cSplunky #include <unistd.h>
566448a78cSplunky #include <errno.h>
576448a78cSplunky
586448a78cSplunky #include "event-internal.h"
596ecf6635Schristos #include "evsignal-internal.h"
606ecf6635Schristos #include "event2/thread.h"
616ecf6635Schristos #include "evthread-internal.h"
626ecf6635Schristos #include "log-internal.h"
636ecf6635Schristos #include "evmap-internal.h"
646448a78cSplunky
650d738af4Schristos #ifndef EVENT__HAVE_FD_MASK
666ecf6635Schristos /* This type is mandatory, but Android doesn't define it. */
676ecf6635Schristos typedef unsigned long fd_mask;
686448a78cSplunky #endif
696448a78cSplunky
706ecf6635Schristos #ifndef NFDBITS
716ecf6635Schristos #define NFDBITS (sizeof(fd_mask)*8)
726ecf6635Schristos #endif
736ecf6635Schristos
746ecf6635Schristos /* Divide positive x by y, rounding up. */
756ecf6635Schristos #define DIV_ROUNDUP(x, y) (((x)+((y)-1))/(y))
766ecf6635Schristos
776ecf6635Schristos /* How many bytes to allocate for N fds? */
786ecf6635Schristos #define SELECT_ALLOC_SIZE(n) \
796ecf6635Schristos (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
806ecf6635Schristos
816448a78cSplunky struct selectop {
826448a78cSplunky int event_fds; /* Highest fd in fd set */
836448a78cSplunky int event_fdsz;
846ecf6635Schristos int resize_out_sets;
856448a78cSplunky fd_set *event_readset_in;
866448a78cSplunky fd_set *event_writeset_in;
876448a78cSplunky fd_set *event_readset_out;
886448a78cSplunky fd_set *event_writeset_out;
896448a78cSplunky };
906448a78cSplunky
916448a78cSplunky static void *select_init(struct event_base *);
926ecf6635Schristos static int select_add(struct event_base *, int, short old, short events, void*);
936ecf6635Schristos static int select_del(struct event_base *, int, short old, short events, void*);
946ecf6635Schristos static int select_dispatch(struct event_base *, struct timeval *);
956ecf6635Schristos static void select_dealloc(struct event_base *);
966448a78cSplunky
976448a78cSplunky const struct eventop selectops = {
986448a78cSplunky "select",
996448a78cSplunky select_init,
1006448a78cSplunky select_add,
1016448a78cSplunky select_del,
1026448a78cSplunky select_dispatch,
1036448a78cSplunky select_dealloc,
1047e68cdd7Schristos 1, /* need_reinit. */
1056ecf6635Schristos EV_FEATURE_FDS,
1066ecf6635Schristos 0,
1076448a78cSplunky };
1086448a78cSplunky
1096448a78cSplunky static int select_resize(struct selectop *sop, int fdsz);
1106ecf6635Schristos static void select_free_selectop(struct selectop *sop);
1116448a78cSplunky
1126448a78cSplunky static void *
select_init(struct event_base * base)1136448a78cSplunky select_init(struct event_base *base)
1146448a78cSplunky {
1156448a78cSplunky struct selectop *sop;
1166448a78cSplunky
1176ecf6635Schristos if (!(sop = mm_calloc(1, sizeof(struct selectop))))
1186448a78cSplunky return (NULL);
1196448a78cSplunky
1206ecf6635Schristos if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
1216ecf6635Schristos select_free_selectop(sop);
1226448a78cSplunky return (NULL);
1236ecf6635Schristos }
1246448a78cSplunky
1250d738af4Schristos evsig_init_(base);
1260d738af4Schristos
1270d738af4Schristos evutil_weakrand_seed_(&base->weakrand_seed, 0);
1286448a78cSplunky
1296448a78cSplunky return (sop);
1306448a78cSplunky }
1316448a78cSplunky
1326448a78cSplunky #ifdef CHECK_INVARIANTS
1336448a78cSplunky static void
check_selectop(struct selectop * sop)1346448a78cSplunky check_selectop(struct selectop *sop)
1356448a78cSplunky {
1366ecf6635Schristos /* nothing to be done here */
1376448a78cSplunky }
1386448a78cSplunky #else
139*81d2345cSrillig #define check_selectop(sop) do { (void) sop; } while (0)
1406448a78cSplunky #endif
1416448a78cSplunky
1426448a78cSplunky static int
select_dispatch(struct event_base * base,struct timeval * tv)1436ecf6635Schristos select_dispatch(struct event_base *base, struct timeval *tv)
1446448a78cSplunky {
1456ecf6635Schristos int res=0, i, j, nfds;
1466ecf6635Schristos struct selectop *sop = base->evbase;
1476448a78cSplunky
1486448a78cSplunky check_selectop(sop);
1496ecf6635Schristos if (sop->resize_out_sets) {
1506ecf6635Schristos fd_set *readset_out=NULL, *writeset_out=NULL;
1516ecf6635Schristos size_t sz = sop->event_fdsz;
1526ecf6635Schristos if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
1536ecf6635Schristos return (-1);
1546ecf6635Schristos sop->event_readset_out = readset_out;
1556ecf6635Schristos if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
1566ecf6635Schristos /* We don't free readset_out here, since it was
1576ecf6635Schristos * already successfully reallocated. The next time
1586ecf6635Schristos * we call select_dispatch, the realloc will be a
1596ecf6635Schristos * no-op. */
1606ecf6635Schristos return (-1);
1616ecf6635Schristos }
1626ecf6635Schristos sop->event_writeset_out = writeset_out;
1636ecf6635Schristos sop->resize_out_sets = 0;
1646ecf6635Schristos }
1656448a78cSplunky
1666448a78cSplunky memcpy(sop->event_readset_out, sop->event_readset_in,
1676448a78cSplunky sop->event_fdsz);
1686448a78cSplunky memcpy(sop->event_writeset_out, sop->event_writeset_in,
1696448a78cSplunky sop->event_fdsz);
1706448a78cSplunky
1716ecf6635Schristos nfds = sop->event_fds+1;
1726ecf6635Schristos
1736ecf6635Schristos EVBASE_RELEASE_LOCK(base, th_base_lock);
1746ecf6635Schristos
1756ecf6635Schristos res = select(nfds, sop->event_readset_out,
1766448a78cSplunky sop->event_writeset_out, NULL, tv);
1776448a78cSplunky
1786ecf6635Schristos EVBASE_ACQUIRE_LOCK(base, th_base_lock);
1796ecf6635Schristos
1806448a78cSplunky check_selectop(sop);
1816448a78cSplunky
1826448a78cSplunky if (res == -1) {
1836448a78cSplunky if (errno != EINTR) {
1846448a78cSplunky event_warn("select");
1856448a78cSplunky return (-1);
1866448a78cSplunky }
1876448a78cSplunky
1886448a78cSplunky return (0);
1896448a78cSplunky }
1906448a78cSplunky
1916448a78cSplunky event_debug(("%s: select reports %d", __func__, res));
1926448a78cSplunky
1936448a78cSplunky check_selectop(sop);
1940d738af4Schristos i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
1956ecf6635Schristos for (j = 0; j < nfds; ++j) {
1966ecf6635Schristos if (++i >= nfds)
1976448a78cSplunky i = 0;
1986448a78cSplunky res = 0;
1996ecf6635Schristos if (FD_ISSET(i, sop->event_readset_out))
2006448a78cSplunky res |= EV_READ;
2016ecf6635Schristos if (FD_ISSET(i, sop->event_writeset_out))
2026448a78cSplunky res |= EV_WRITE;
2036ecf6635Schristos
2046ecf6635Schristos if (res == 0)
2056ecf6635Schristos continue;
2066ecf6635Schristos
2070d738af4Schristos evmap_io_active_(base, i, res);
2086448a78cSplunky }
2096448a78cSplunky check_selectop(sop);
2106448a78cSplunky
2116448a78cSplunky return (0);
2126448a78cSplunky }
2136448a78cSplunky
2146448a78cSplunky static int
select_resize(struct selectop * sop,int fdsz)2156448a78cSplunky select_resize(struct selectop *sop, int fdsz)
2166448a78cSplunky {
2176448a78cSplunky fd_set *readset_in = NULL;
2186448a78cSplunky fd_set *writeset_in = NULL;
2196448a78cSplunky
2206448a78cSplunky if (sop->event_readset_in)
2216448a78cSplunky check_selectop(sop);
2226448a78cSplunky
2236ecf6635Schristos if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
2246448a78cSplunky goto error;
2256448a78cSplunky sop->event_readset_in = readset_in;
2266ecf6635Schristos if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
2276ecf6635Schristos /* Note that this will leave event_readset_in expanded.
2286ecf6635Schristos * That's okay; we wouldn't want to free it, since that would
2296ecf6635Schristos * change the semantics of select_resize from "expand the
2306ecf6635Schristos * readset_in and writeset_in, or return -1" to "expand the
2316ecf6635Schristos * *set_in members, or trash them and return -1."
2326ecf6635Schristos */
2336448a78cSplunky goto error;
2346ecf6635Schristos }
2356448a78cSplunky sop->event_writeset_in = writeset_in;
2366ecf6635Schristos sop->resize_out_sets = 1;
2376448a78cSplunky
2386448a78cSplunky memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
2396448a78cSplunky fdsz - sop->event_fdsz);
2406448a78cSplunky memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
2416448a78cSplunky fdsz - sop->event_fdsz);
2426448a78cSplunky
2436448a78cSplunky sop->event_fdsz = fdsz;
2446448a78cSplunky check_selectop(sop);
2456448a78cSplunky
2466448a78cSplunky return (0);
2476448a78cSplunky
2486448a78cSplunky error:
2496448a78cSplunky event_warn("malloc");
2506448a78cSplunky return (-1);
2516448a78cSplunky }
2526448a78cSplunky
2536448a78cSplunky
2546448a78cSplunky static int
select_add(struct event_base * base,int fd,short old,short events,void * p)2556ecf6635Schristos select_add(struct event_base *base, int fd, short old, short events, void *p)
2566448a78cSplunky {
2576ecf6635Schristos struct selectop *sop = base->evbase;
2586ecf6635Schristos (void) p;
2596448a78cSplunky
2606ecf6635Schristos EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
2616448a78cSplunky check_selectop(sop);
2626448a78cSplunky /*
2636448a78cSplunky * Keep track of the highest fd, so that we can calculate the size
2646448a78cSplunky * of the fd_sets for select(2)
2656448a78cSplunky */
2666ecf6635Schristos if (sop->event_fds < fd) {
2676448a78cSplunky int fdsz = sop->event_fdsz;
2686448a78cSplunky
2696ecf6635Schristos if (fdsz < (int)sizeof(fd_mask))
2706ecf6635Schristos fdsz = (int)sizeof(fd_mask);
2716448a78cSplunky
2726ecf6635Schristos /* In theory we should worry about overflow here. In
2736ecf6635Schristos * reality, though, the highest fd on a unixy system will
2746ecf6635Schristos * not overflow here. XXXX */
2756ecf6635Schristos while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
2766448a78cSplunky fdsz *= 2;
2776448a78cSplunky
2786448a78cSplunky if (fdsz != sop->event_fdsz) {
2796448a78cSplunky if (select_resize(sop, fdsz)) {
2806448a78cSplunky check_selectop(sop);
2816448a78cSplunky return (-1);
2826448a78cSplunky }
2836448a78cSplunky }
2846448a78cSplunky
2856ecf6635Schristos sop->event_fds = fd;
2866448a78cSplunky }
2876448a78cSplunky
2886ecf6635Schristos if (events & EV_READ)
2896ecf6635Schristos FD_SET(fd, sop->event_readset_in);
2906ecf6635Schristos if (events & EV_WRITE)
2916ecf6635Schristos FD_SET(fd, sop->event_writeset_in);
2926448a78cSplunky check_selectop(sop);
2936448a78cSplunky
2946448a78cSplunky return (0);
2956448a78cSplunky }
2966448a78cSplunky
2976448a78cSplunky /*
2986448a78cSplunky * Nothing to be done here.
2996448a78cSplunky */
3006448a78cSplunky
3016448a78cSplunky static int
select_del(struct event_base * base,int fd,short old,short events,void * p)3026ecf6635Schristos select_del(struct event_base *base, int fd, short old, short events, void *p)
3036448a78cSplunky {
3046ecf6635Schristos struct selectop *sop = base->evbase;
3056ecf6635Schristos (void)p;
3066448a78cSplunky
3076ecf6635Schristos EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
3086448a78cSplunky check_selectop(sop);
3096448a78cSplunky
3106ecf6635Schristos if (sop->event_fds < fd) {
3116448a78cSplunky check_selectop(sop);
3126448a78cSplunky return (0);
3136448a78cSplunky }
3146448a78cSplunky
3156ecf6635Schristos if (events & EV_READ)
3166ecf6635Schristos FD_CLR(fd, sop->event_readset_in);
3176448a78cSplunky
3186ecf6635Schristos if (events & EV_WRITE)
3196ecf6635Schristos FD_CLR(fd, sop->event_writeset_in);
3206448a78cSplunky
3216448a78cSplunky check_selectop(sop);
3226448a78cSplunky return (0);
3236448a78cSplunky }
3246448a78cSplunky
3256448a78cSplunky static void
select_free_selectop(struct selectop * sop)3266ecf6635Schristos select_free_selectop(struct selectop *sop)
3276448a78cSplunky {
3286448a78cSplunky if (sop->event_readset_in)
3296ecf6635Schristos mm_free(sop->event_readset_in);
3306448a78cSplunky if (sop->event_writeset_in)
3316ecf6635Schristos mm_free(sop->event_writeset_in);
3326448a78cSplunky if (sop->event_readset_out)
3336ecf6635Schristos mm_free(sop->event_readset_out);
3346448a78cSplunky if (sop->event_writeset_out)
3356ecf6635Schristos mm_free(sop->event_writeset_out);
3366448a78cSplunky
3376448a78cSplunky memset(sop, 0, sizeof(struct selectop));
3386ecf6635Schristos mm_free(sop);
3396ecf6635Schristos }
3406ecf6635Schristos
3416ecf6635Schristos static void
select_dealloc(struct event_base * base)3426ecf6635Schristos select_dealloc(struct event_base *base)
3436ecf6635Schristos {
3440d738af4Schristos evsig_dealloc_(base);
3456ecf6635Schristos
3466ecf6635Schristos select_free_selectop(base->evbase);
3476448a78cSplunky }
3480d738af4Schristos
3490d738af4Schristos #endif /* EVENT__HAVE_SELECT */
350