xref: /netbsd-src/external/bsd/libevent/dist/select.c (revision 81d2345cc4f87f54efa9edec2199119599391d4b)
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