xref: /freebsd/contrib/ntp/sntp/libevent/select.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
12b15cb3dSCy Schubert /*	$OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $	*/
22b15cb3dSCy Schubert 
32b15cb3dSCy Schubert /*
42b15cb3dSCy Schubert  * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
52b15cb3dSCy Schubert  * Copyright 2007-2012 Niels Provos and Nick Mathewson
62b15cb3dSCy Schubert  *
72b15cb3dSCy Schubert  * Redistribution and use in source and binary forms, with or without
82b15cb3dSCy Schubert  * modification, are permitted provided that the following conditions
92b15cb3dSCy Schubert  * are met:
102b15cb3dSCy Schubert  * 1. Redistributions of source code must retain the above copyright
112b15cb3dSCy Schubert  *    notice, this list of conditions and the following disclaimer.
122b15cb3dSCy Schubert  * 2. Redistributions in binary form must reproduce the above copyright
132b15cb3dSCy Schubert  *    notice, this list of conditions and the following disclaimer in the
142b15cb3dSCy Schubert  *    documentation and/or other materials provided with the distribution.
152b15cb3dSCy Schubert  * 3. The name of the author may not be used to endorse or promote products
162b15cb3dSCy Schubert  *    derived from this software without specific prior written permission.
172b15cb3dSCy Schubert  *
182b15cb3dSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
192b15cb3dSCy Schubert  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
202b15cb3dSCy Schubert  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
212b15cb3dSCy Schubert  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
222b15cb3dSCy Schubert  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
232b15cb3dSCy Schubert  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
242b15cb3dSCy Schubert  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
252b15cb3dSCy Schubert  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
262b15cb3dSCy Schubert  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
272b15cb3dSCy Schubert  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282b15cb3dSCy Schubert  */
292b15cb3dSCy Schubert #include "event2/event-config.h"
302b15cb3dSCy Schubert #include "evconfig-private.h"
312b15cb3dSCy Schubert 
322b15cb3dSCy Schubert #ifdef EVENT__HAVE_SELECT
332b15cb3dSCy Schubert 
342b15cb3dSCy Schubert #ifdef __APPLE__
352b15cb3dSCy Schubert /* Apple wants us to define this if we might ever pass more than
362b15cb3dSCy Schubert  * FD_SETSIZE bits to select(). */
372b15cb3dSCy Schubert #define _DARWIN_UNLIMITED_SELECT
382b15cb3dSCy Schubert #endif
392b15cb3dSCy Schubert 
402b15cb3dSCy Schubert #include <sys/types.h>
412b15cb3dSCy Schubert #ifdef EVENT__HAVE_SYS_TIME_H
422b15cb3dSCy Schubert #include <sys/time.h>
432b15cb3dSCy Schubert #endif
442b15cb3dSCy Schubert #ifdef EVENT__HAVE_SYS_SELECT_H
452b15cb3dSCy Schubert #include <sys/select.h>
462b15cb3dSCy Schubert #endif
472b15cb3dSCy Schubert #include <sys/queue.h>
482b15cb3dSCy Schubert #include <signal.h>
492b15cb3dSCy Schubert #include <stdio.h>
502b15cb3dSCy Schubert #include <stdlib.h>
512b15cb3dSCy Schubert #include <string.h>
522b15cb3dSCy Schubert #include <unistd.h>
532b15cb3dSCy Schubert #include <errno.h>
542b15cb3dSCy Schubert 
552b15cb3dSCy Schubert #include "event-internal.h"
562b15cb3dSCy Schubert #include "evsignal-internal.h"
572b15cb3dSCy Schubert #include "event2/thread.h"
582b15cb3dSCy Schubert #include "evthread-internal.h"
592b15cb3dSCy Schubert #include "log-internal.h"
602b15cb3dSCy Schubert #include "evmap-internal.h"
612b15cb3dSCy Schubert 
622b15cb3dSCy Schubert #ifndef EVENT__HAVE_FD_MASK
632b15cb3dSCy Schubert /* This type is mandatory, but Android doesn't define it. */
642b15cb3dSCy Schubert typedef unsigned long fd_mask;
652b15cb3dSCy Schubert #endif
662b15cb3dSCy Schubert 
672b15cb3dSCy Schubert #ifndef NFDBITS
682b15cb3dSCy Schubert #define NFDBITS (sizeof(fd_mask)*8)
692b15cb3dSCy Schubert #endif
702b15cb3dSCy Schubert 
712b15cb3dSCy Schubert /* Divide positive x by y, rounding up. */
722b15cb3dSCy Schubert #define DIV_ROUNDUP(x, y)   (((x)+((y)-1))/(y))
732b15cb3dSCy Schubert 
742b15cb3dSCy Schubert /* How many bytes to allocate for N fds? */
752b15cb3dSCy Schubert #define SELECT_ALLOC_SIZE(n) \
762b15cb3dSCy Schubert 	(DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
772b15cb3dSCy Schubert 
782b15cb3dSCy Schubert struct selectop {
792b15cb3dSCy Schubert 	int event_fds;		/* Highest fd in fd set */
802b15cb3dSCy Schubert 	int event_fdsz;
812b15cb3dSCy Schubert 	int resize_out_sets;
822b15cb3dSCy Schubert 	fd_set *event_readset_in;
832b15cb3dSCy Schubert 	fd_set *event_writeset_in;
842b15cb3dSCy Schubert 	fd_set *event_readset_out;
852b15cb3dSCy Schubert 	fd_set *event_writeset_out;
862b15cb3dSCy Schubert };
872b15cb3dSCy Schubert 
882b15cb3dSCy Schubert static void *select_init(struct event_base *);
892b15cb3dSCy Schubert static int select_add(struct event_base *, int, short old, short events, void*);
902b15cb3dSCy Schubert static int select_del(struct event_base *, int, short old, short events, void*);
912b15cb3dSCy Schubert static int select_dispatch(struct event_base *, struct timeval *);
922b15cb3dSCy Schubert static void select_dealloc(struct event_base *);
932b15cb3dSCy Schubert 
942b15cb3dSCy Schubert const struct eventop selectops = {
952b15cb3dSCy Schubert 	"select",
962b15cb3dSCy Schubert 	select_init,
972b15cb3dSCy Schubert 	select_add,
982b15cb3dSCy Schubert 	select_del,
992b15cb3dSCy Schubert 	select_dispatch,
1002b15cb3dSCy Schubert 	select_dealloc,
101*a466cc55SCy Schubert 	1, /* need_reinit. */
1022b15cb3dSCy Schubert 	EV_FEATURE_FDS,
1032b15cb3dSCy Schubert 	0,
1042b15cb3dSCy Schubert };
1052b15cb3dSCy Schubert 
1062b15cb3dSCy Schubert static int select_resize(struct selectop *sop, int fdsz);
1072b15cb3dSCy Schubert static void select_free_selectop(struct selectop *sop);
1082b15cb3dSCy Schubert 
1092b15cb3dSCy Schubert static void *
select_init(struct event_base * base)1102b15cb3dSCy Schubert select_init(struct event_base *base)
1112b15cb3dSCy Schubert {
1122b15cb3dSCy Schubert 	struct selectop *sop;
1132b15cb3dSCy Schubert 
1142b15cb3dSCy Schubert 	if (!(sop = mm_calloc(1, sizeof(struct selectop))))
1152b15cb3dSCy Schubert 		return (NULL);
1162b15cb3dSCy Schubert 
1172b15cb3dSCy Schubert 	if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
1182b15cb3dSCy Schubert 		select_free_selectop(sop);
1192b15cb3dSCy Schubert 		return (NULL);
1202b15cb3dSCy Schubert 	}
1212b15cb3dSCy Schubert 
1222b15cb3dSCy Schubert 	evsig_init_(base);
1232b15cb3dSCy Schubert 
1242b15cb3dSCy Schubert 	evutil_weakrand_seed_(&base->weakrand_seed, 0);
1252b15cb3dSCy Schubert 
1262b15cb3dSCy Schubert 	return (sop);
1272b15cb3dSCy Schubert }
1282b15cb3dSCy Schubert 
1292b15cb3dSCy Schubert #ifdef CHECK_INVARIANTS
1302b15cb3dSCy Schubert static void
check_selectop(struct selectop * sop)1312b15cb3dSCy Schubert check_selectop(struct selectop *sop)
1322b15cb3dSCy Schubert {
1332b15cb3dSCy Schubert 	/* nothing to be done here */
1342b15cb3dSCy Schubert }
1352b15cb3dSCy Schubert #else
1362b15cb3dSCy Schubert #define check_selectop(sop) do { (void) sop; } while (0)
1372b15cb3dSCy Schubert #endif
1382b15cb3dSCy Schubert 
1392b15cb3dSCy Schubert static int
select_dispatch(struct event_base * base,struct timeval * tv)1402b15cb3dSCy Schubert select_dispatch(struct event_base *base, struct timeval *tv)
1412b15cb3dSCy Schubert {
1422b15cb3dSCy Schubert 	int res=0, i, j, nfds;
1432b15cb3dSCy Schubert 	struct selectop *sop = base->evbase;
1442b15cb3dSCy Schubert 
1452b15cb3dSCy Schubert 	check_selectop(sop);
1462b15cb3dSCy Schubert 	if (sop->resize_out_sets) {
1472b15cb3dSCy Schubert 		fd_set *readset_out=NULL, *writeset_out=NULL;
1482b15cb3dSCy Schubert 		size_t sz = sop->event_fdsz;
1492b15cb3dSCy Schubert 		if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
1502b15cb3dSCy Schubert 			return (-1);
1512b15cb3dSCy Schubert 		sop->event_readset_out = readset_out;
1522b15cb3dSCy Schubert 		if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
1532b15cb3dSCy Schubert 			/* We don't free readset_out here, since it was
1542b15cb3dSCy Schubert 			 * already successfully reallocated. The next time
1552b15cb3dSCy Schubert 			 * we call select_dispatch, the realloc will be a
1562b15cb3dSCy Schubert 			 * no-op. */
1572b15cb3dSCy Schubert 			return (-1);
1582b15cb3dSCy Schubert 		}
1592b15cb3dSCy Schubert 		sop->event_writeset_out = writeset_out;
1602b15cb3dSCy Schubert 		sop->resize_out_sets = 0;
1612b15cb3dSCy Schubert 	}
1622b15cb3dSCy Schubert 
1632b15cb3dSCy Schubert 	memcpy(sop->event_readset_out, sop->event_readset_in,
1642b15cb3dSCy Schubert 	       sop->event_fdsz);
1652b15cb3dSCy Schubert 	memcpy(sop->event_writeset_out, sop->event_writeset_in,
1662b15cb3dSCy Schubert 	       sop->event_fdsz);
1672b15cb3dSCy Schubert 
1682b15cb3dSCy Schubert 	nfds = sop->event_fds+1;
1692b15cb3dSCy Schubert 
1702b15cb3dSCy Schubert 	EVBASE_RELEASE_LOCK(base, th_base_lock);
1712b15cb3dSCy Schubert 
1722b15cb3dSCy Schubert 	res = select(nfds, sop->event_readset_out,
1732b15cb3dSCy Schubert 	    sop->event_writeset_out, NULL, tv);
1742b15cb3dSCy Schubert 
1752b15cb3dSCy Schubert 	EVBASE_ACQUIRE_LOCK(base, th_base_lock);
1762b15cb3dSCy Schubert 
1772b15cb3dSCy Schubert 	check_selectop(sop);
1782b15cb3dSCy Schubert 
1792b15cb3dSCy Schubert 	if (res == -1) {
1802b15cb3dSCy Schubert 		if (errno != EINTR) {
1812b15cb3dSCy Schubert 			event_warn("select");
1822b15cb3dSCy Schubert 			return (-1);
1832b15cb3dSCy Schubert 		}
1842b15cb3dSCy Schubert 
1852b15cb3dSCy Schubert 		return (0);
1862b15cb3dSCy Schubert 	}
1872b15cb3dSCy Schubert 
1882b15cb3dSCy Schubert 	event_debug(("%s: select reports %d", __func__, res));
1892b15cb3dSCy Schubert 
1902b15cb3dSCy Schubert 	check_selectop(sop);
1912b15cb3dSCy Schubert 	i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
1922b15cb3dSCy Schubert 	for (j = 0; j < nfds; ++j) {
1932b15cb3dSCy Schubert 		if (++i >= nfds)
1942b15cb3dSCy Schubert 			i = 0;
1952b15cb3dSCy Schubert 		res = 0;
1962b15cb3dSCy Schubert 		if (FD_ISSET(i, sop->event_readset_out))
1972b15cb3dSCy Schubert 			res |= EV_READ;
1982b15cb3dSCy Schubert 		if (FD_ISSET(i, sop->event_writeset_out))
1992b15cb3dSCy Schubert 			res |= EV_WRITE;
2002b15cb3dSCy Schubert 
2012b15cb3dSCy Schubert 		if (res == 0)
2022b15cb3dSCy Schubert 			continue;
2032b15cb3dSCy Schubert 
2042b15cb3dSCy Schubert 		evmap_io_active_(base, i, res);
2052b15cb3dSCy Schubert 	}
2062b15cb3dSCy Schubert 	check_selectop(sop);
2072b15cb3dSCy Schubert 
2082b15cb3dSCy Schubert 	return (0);
2092b15cb3dSCy Schubert }
2102b15cb3dSCy Schubert 
2112b15cb3dSCy Schubert static int
select_resize(struct selectop * sop,int fdsz)2122b15cb3dSCy Schubert select_resize(struct selectop *sop, int fdsz)
2132b15cb3dSCy Schubert {
2142b15cb3dSCy Schubert 	fd_set *readset_in = NULL;
2152b15cb3dSCy Schubert 	fd_set *writeset_in = NULL;
2162b15cb3dSCy Schubert 
2172b15cb3dSCy Schubert 	if (sop->event_readset_in)
2182b15cb3dSCy Schubert 		check_selectop(sop);
2192b15cb3dSCy Schubert 
2202b15cb3dSCy Schubert 	if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
2212b15cb3dSCy Schubert 		goto error;
2222b15cb3dSCy Schubert 	sop->event_readset_in = readset_in;
2232b15cb3dSCy Schubert 	if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
2242b15cb3dSCy Schubert 		/* Note that this will leave event_readset_in expanded.
2252b15cb3dSCy Schubert 		 * That's okay; we wouldn't want to free it, since that would
2262b15cb3dSCy Schubert 		 * change the semantics of select_resize from "expand the
2272b15cb3dSCy Schubert 		 * readset_in and writeset_in, or return -1" to "expand the
2282b15cb3dSCy Schubert 		 * *set_in members, or trash them and return -1."
2292b15cb3dSCy Schubert 		 */
2302b15cb3dSCy Schubert 		goto error;
2312b15cb3dSCy Schubert 	}
2322b15cb3dSCy Schubert 	sop->event_writeset_in = writeset_in;
2332b15cb3dSCy Schubert 	sop->resize_out_sets = 1;
2342b15cb3dSCy Schubert 
2352b15cb3dSCy Schubert 	memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
2362b15cb3dSCy Schubert 	    fdsz - sop->event_fdsz);
2372b15cb3dSCy Schubert 	memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
2382b15cb3dSCy Schubert 	    fdsz - sop->event_fdsz);
2392b15cb3dSCy Schubert 
2402b15cb3dSCy Schubert 	sop->event_fdsz = fdsz;
2412b15cb3dSCy Schubert 	check_selectop(sop);
2422b15cb3dSCy Schubert 
2432b15cb3dSCy Schubert 	return (0);
2442b15cb3dSCy Schubert 
2452b15cb3dSCy Schubert  error:
2462b15cb3dSCy Schubert 	event_warn("malloc");
2472b15cb3dSCy Schubert 	return (-1);
2482b15cb3dSCy Schubert }
2492b15cb3dSCy Schubert 
2502b15cb3dSCy Schubert 
2512b15cb3dSCy Schubert static int
select_add(struct event_base * base,int fd,short old,short events,void * p)2522b15cb3dSCy Schubert select_add(struct event_base *base, int fd, short old, short events, void *p)
2532b15cb3dSCy Schubert {
2542b15cb3dSCy Schubert 	struct selectop *sop = base->evbase;
2552b15cb3dSCy Schubert 	(void) p;
2562b15cb3dSCy Schubert 
2572b15cb3dSCy Schubert 	EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
2582b15cb3dSCy Schubert 	check_selectop(sop);
2592b15cb3dSCy Schubert 	/*
2602b15cb3dSCy Schubert 	 * Keep track of the highest fd, so that we can calculate the size
2612b15cb3dSCy Schubert 	 * of the fd_sets for select(2)
2622b15cb3dSCy Schubert 	 */
2632b15cb3dSCy Schubert 	if (sop->event_fds < fd) {
2642b15cb3dSCy Schubert 		int fdsz = sop->event_fdsz;
2652b15cb3dSCy Schubert 
2662b15cb3dSCy Schubert 		if (fdsz < (int)sizeof(fd_mask))
2672b15cb3dSCy Schubert 			fdsz = (int)sizeof(fd_mask);
2682b15cb3dSCy Schubert 
2692b15cb3dSCy Schubert 		/* In theory we should worry about overflow here.  In
2702b15cb3dSCy Schubert 		 * reality, though, the highest fd on a unixy system will
2712b15cb3dSCy Schubert 		 * not overflow here. XXXX */
2722b15cb3dSCy Schubert 		while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
2732b15cb3dSCy Schubert 			fdsz *= 2;
2742b15cb3dSCy Schubert 
2752b15cb3dSCy Schubert 		if (fdsz != sop->event_fdsz) {
2762b15cb3dSCy Schubert 			if (select_resize(sop, fdsz)) {
2772b15cb3dSCy Schubert 				check_selectop(sop);
2782b15cb3dSCy Schubert 				return (-1);
2792b15cb3dSCy Schubert 			}
2802b15cb3dSCy Schubert 		}
2812b15cb3dSCy Schubert 
2822b15cb3dSCy Schubert 		sop->event_fds = fd;
2832b15cb3dSCy Schubert 	}
2842b15cb3dSCy Schubert 
2852b15cb3dSCy Schubert 	if (events & EV_READ)
2862b15cb3dSCy Schubert 		FD_SET(fd, sop->event_readset_in);
2872b15cb3dSCy Schubert 	if (events & EV_WRITE)
2882b15cb3dSCy Schubert 		FD_SET(fd, sop->event_writeset_in);
2892b15cb3dSCy Schubert 	check_selectop(sop);
2902b15cb3dSCy Schubert 
2912b15cb3dSCy Schubert 	return (0);
2922b15cb3dSCy Schubert }
2932b15cb3dSCy Schubert 
2942b15cb3dSCy Schubert /*
2952b15cb3dSCy Schubert  * Nothing to be done here.
2962b15cb3dSCy Schubert  */
2972b15cb3dSCy Schubert 
2982b15cb3dSCy Schubert static int
select_del(struct event_base * base,int fd,short old,short events,void * p)2992b15cb3dSCy Schubert select_del(struct event_base *base, int fd, short old, short events, void *p)
3002b15cb3dSCy Schubert {
3012b15cb3dSCy Schubert 	struct selectop *sop = base->evbase;
3022b15cb3dSCy Schubert 	(void)p;
3032b15cb3dSCy Schubert 
3042b15cb3dSCy Schubert 	EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
3052b15cb3dSCy Schubert 	check_selectop(sop);
3062b15cb3dSCy Schubert 
3072b15cb3dSCy Schubert 	if (sop->event_fds < fd) {
3082b15cb3dSCy Schubert 		check_selectop(sop);
3092b15cb3dSCy Schubert 		return (0);
3102b15cb3dSCy Schubert 	}
3112b15cb3dSCy Schubert 
3122b15cb3dSCy Schubert 	if (events & EV_READ)
3132b15cb3dSCy Schubert 		FD_CLR(fd, sop->event_readset_in);
3142b15cb3dSCy Schubert 
3152b15cb3dSCy Schubert 	if (events & EV_WRITE)
3162b15cb3dSCy Schubert 		FD_CLR(fd, sop->event_writeset_in);
3172b15cb3dSCy Schubert 
3182b15cb3dSCy Schubert 	check_selectop(sop);
3192b15cb3dSCy Schubert 	return (0);
3202b15cb3dSCy Schubert }
3212b15cb3dSCy Schubert 
3222b15cb3dSCy Schubert static void
select_free_selectop(struct selectop * sop)3232b15cb3dSCy Schubert select_free_selectop(struct selectop *sop)
3242b15cb3dSCy Schubert {
3252b15cb3dSCy Schubert 	if (sop->event_readset_in)
3262b15cb3dSCy Schubert 		mm_free(sop->event_readset_in);
3272b15cb3dSCy Schubert 	if (sop->event_writeset_in)
3282b15cb3dSCy Schubert 		mm_free(sop->event_writeset_in);
3292b15cb3dSCy Schubert 	if (sop->event_readset_out)
3302b15cb3dSCy Schubert 		mm_free(sop->event_readset_out);
3312b15cb3dSCy Schubert 	if (sop->event_writeset_out)
3322b15cb3dSCy Schubert 		mm_free(sop->event_writeset_out);
3332b15cb3dSCy Schubert 
3342b15cb3dSCy Schubert 	memset(sop, 0, sizeof(struct selectop));
3352b15cb3dSCy Schubert 	mm_free(sop);
3362b15cb3dSCy Schubert }
3372b15cb3dSCy Schubert 
3382b15cb3dSCy Schubert static void
select_dealloc(struct event_base * base)3392b15cb3dSCy Schubert select_dealloc(struct event_base *base)
3402b15cb3dSCy Schubert {
3412b15cb3dSCy Schubert 	evsig_dealloc_(base);
3422b15cb3dSCy Schubert 
3432b15cb3dSCy Schubert 	select_free_selectop(base->evbase);
3442b15cb3dSCy Schubert }
3452b15cb3dSCy Schubert 
3462b15cb3dSCy Schubert #endif /* EVENT__HAVE_SELECT */
347