15f865858SNiels Provos /* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
25f865858SNiels Provos
3aa6567feSNiels Provos /*
4b85b710cSNick Mathewson * Copyright 2000-2007 Niels Provos <[email protected]>
5e49e2891SNick Mathewson * Copyright 2007-2012 Niels Provos and Nick Mathewson
6aa6567feSNiels Provos *
7aa6567feSNiels Provos * Redistribution and use in source and binary forms, with or without
8aa6567feSNiels Provos * modification, are permitted provided that the following conditions
9aa6567feSNiels Provos * are met:
10aa6567feSNiels Provos * 1. Redistributions of source code must retain the above copyright
11aa6567feSNiels Provos * notice, this list of conditions and the following disclaimer.
12aa6567feSNiels Provos * 2. Redistributions in binary form must reproduce the above copyright
13aa6567feSNiels Provos * notice, this list of conditions and the following disclaimer in the
14aa6567feSNiels Provos * documentation and/or other materials provided with the distribution.
15c3f496c7SNiels Provos * 3. The name of the author may not be used to endorse or promote products
16aa6567feSNiels Provos * derived from this software without specific prior written permission.
17aa6567feSNiels Provos *
18aa6567feSNiels Provos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19aa6567feSNiels Provos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20aa6567feSNiels Provos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21aa6567feSNiels Provos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22aa6567feSNiels Provos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23aa6567feSNiels Provos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24aa6567feSNiels Provos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25aa6567feSNiels Provos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26aa6567feSNiels Provos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27aa6567feSNiels Provos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28aa6567feSNiels Provos */
29ec347b92SNick Mathewson #include "event2/event-config.h"
300915ca0aSKevin Bowling #include "evconfig-private.h"
31aa6567feSNiels Provos
3268120d9bSNick Mathewson #ifdef EVENT__HAVE_SELECT
3376d4c929SRoss Lagerwall
341fb5cc68SNick Mathewson #ifdef __APPLE__
351fb5cc68SNick Mathewson /* Apple wants us to define this if we might ever pass more than
361fb5cc68SNick Mathewson * FD_SETSIZE bits to select(). */
371fb5cc68SNick Mathewson #define _DARWIN_UNLIMITED_SELECT
381fb5cc68SNick Mathewson #endif
391fb5cc68SNick Mathewson
40aa6567feSNiels Provos #include <sys/types.h>
4168120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_TIME_H
42aa6567feSNiels Provos #include <sys/time.h>
439d2401ffSNiels Provos #endif
4468120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_SELECT_H
455e0ac7f2SNiels Provos #include <sys/select.h>
465e0ac7f2SNiels Provos #endif
47aa6567feSNiels Provos #include <sys/queue.h>
48b855bc55SNiels Provos #include <signal.h>
49aa6567feSNiels Provos #include <stdio.h>
50aa6567feSNiels Provos #include <stdlib.h>
51aa6567feSNiels Provos #include <string.h>
52aa6567feSNiels Provos #include <unistd.h>
53aa6567feSNiels Provos #include <errno.h>
54aa6567feSNiels Provos
558773c4c9SNiels Provos #include "event-internal.h"
56169321c9SNick Mathewson #include "evsignal-internal.h"
576b22e74aSNick Mathewson #include "event2/thread.h"
586b22e74aSNick Mathewson #include "evthread-internal.h"
59169321c9SNick Mathewson #include "log-internal.h"
60169321c9SNick Mathewson #include "evmap-internal.h"
61aa6567feSNiels Provos
6268120d9bSNick Mathewson #ifndef EVENT__HAVE_FD_MASK
6318fe4008SNick Mathewson /* This type is mandatory, but Android doesn't define it. */
6418fe4008SNick Mathewson typedef unsigned long fd_mask;
6518fe4008SNick Mathewson #endif
6618fe4008SNick Mathewson
6749d11362SNick Mathewson #ifndef NFDBITS
6849d11362SNick Mathewson #define NFDBITS (sizeof(fd_mask)*8)
6949d11362SNick Mathewson #endif
7049d11362SNick Mathewson
7149d11362SNick Mathewson /* Divide positive x by y, rounding up. */
7249d11362SNick Mathewson #define DIV_ROUNDUP(x, y) (((x)+((y)-1))/(y))
7349d11362SNick Mathewson
7449d11362SNick Mathewson /* How many bytes to allocate for N fds? */
7549d11362SNick Mathewson #define SELECT_ALLOC_SIZE(n) \
7649d11362SNick Mathewson (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
7749d11362SNick Mathewson
78aa6567feSNiels Provos struct selectop {
79aa6567feSNiels Provos int event_fds; /* Highest fd in fd set */
80aa6567feSNiels Provos int event_fdsz;
816b22e74aSNick Mathewson int resize_out_sets;
827a0c530bSNiels Provos fd_set *event_readset_in;
837a0c530bSNiels Provos fd_set *event_writeset_in;
847a0c530bSNiels Provos fd_set *event_readset_out;
857a0c530bSNiels Provos fd_set *event_writeset_out;
863ba224dbSNiels Provos };
87aa6567feSNiels Provos
88ca42671aSNiels Provos static void *select_init(struct event_base *);
89554e1493SNick Mathewson static int select_add(struct event_base *, int, short old, short events, void*);
90554e1493SNick Mathewson static int select_del(struct event_base *, int, short old, short events, void*);
9102b2b4d1SNiels Provos static int select_dispatch(struct event_base *, struct timeval *);
9202b2b4d1SNiels Provos static void select_dealloc(struct event_base *);
93aa6567feSNiels Provos
94e506eaf7SNiels Provos const struct eventop selectops = {
95aa6567feSNiels Provos "select",
96aa6567feSNiels Provos select_init,
97aa6567feSNiels Provos select_add,
98aa6567feSNiels Provos select_del,
992e8051f5SNiels Provos select_dispatch,
100ce4ee418SNick Mathewson select_dealloc,
101*1c9cc07bSAzat Khuzhin 1, /* need_reinit. */
10205965921SNick Mathewson EV_FEATURE_FDS,
103554e1493SNick Mathewson 0,
104aa6567feSNiels Provos };
105aa6567feSNiels Provos
1067a0c530bSNiels Provos static int select_resize(struct selectop *sop, int fdsz);
1070c0ec0beSNick Mathewson static void select_free_selectop(struct selectop *sop);
1087a0c530bSNiels Provos
109ca42671aSNiels Provos static void *
select_init(struct event_base * base)11041b7cbc3SNiels Provos select_init(struct event_base *base)
111aa6567feSNiels Provos {
1123ba224dbSNiels Provos struct selectop *sop;
1133ba224dbSNiels Provos
11449868b61SNick Mathewson if (!(sop = mm_calloc(1, sizeof(struct selectop))))
1153ba224dbSNiels Provos return (NULL);
116aa6567feSNiels Provos
11749d11362SNick Mathewson if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
1180c0ec0beSNick Mathewson select_free_selectop(sop);
119666b0966SJardel Weyrich return (NULL);
120666b0966SJardel Weyrich }
1217a0c530bSNiels Provos
1228ac3c4c2SNick Mathewson evsig_init_(base);
123b855bc55SNiels Provos
1243aa44159SNick Mathewson evutil_weakrand_seed_(&base->weakrand_seed, 0);
1253aa44159SNick Mathewson
1263ba224dbSNiels Provos return (sop);
127aa6567feSNiels Provos }
128aa6567feSNiels Provos
1297a0c530bSNiels Provos #ifdef CHECK_INVARIANTS
1307a0c530bSNiels Provos static void
check_selectop(struct selectop * sop)1317a0c530bSNiels Provos check_selectop(struct selectop *sop)
1327a0c530bSNiels Provos {
13302b2b4d1SNiels Provos /* nothing to be done here */
1347a0c530bSNiels Provos }
1357a0c530bSNiels Provos #else
1368d94bd03SNiels Provos #define check_selectop(sop) do { (void) sop; } while (0)
1377a0c530bSNiels Provos #endif
1387a0c530bSNiels Provos
139ca42671aSNiels Provos static int
select_dispatch(struct event_base * base,struct timeval * tv)14002b2b4d1SNiels Provos select_dispatch(struct event_base *base, struct timeval *tv)
141aa6567feSNiels Provos {
1426b22e74aSNick Mathewson int res=0, i, j, nfds;
14302b2b4d1SNiels Provos struct selectop *sop = base->evbase;
144aa6567feSNiels Provos
1457a0c530bSNiels Provos check_selectop(sop);
1466b22e74aSNick Mathewson if (sop->resize_out_sets) {
1476b22e74aSNick Mathewson fd_set *readset_out=NULL, *writeset_out=NULL;
1486b22e74aSNick Mathewson size_t sz = sop->event_fdsz;
1496b22e74aSNick Mathewson if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
1506b22e74aSNick Mathewson return (-1);
15183e805a4SNick Mathewson sop->event_readset_out = readset_out;
1526b22e74aSNick Mathewson if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
15383e805a4SNick Mathewson /* We don't free readset_out here, since it was
15483e805a4SNick Mathewson * already successfully reallocated. The next time
15583e805a4SNick Mathewson * we call select_dispatch, the realloc will be a
15683e805a4SNick Mathewson * no-op. */
1576b22e74aSNick Mathewson return (-1);
1586b22e74aSNick Mathewson }
1596b22e74aSNick Mathewson sop->event_writeset_out = writeset_out;
1606b22e74aSNick Mathewson sop->resize_out_sets = 0;
1616b22e74aSNick Mathewson }
162aa6567feSNiels Provos
1637a0c530bSNiels Provos memcpy(sop->event_readset_out, sop->event_readset_in,
1647a0c530bSNiels Provos sop->event_fdsz);
1657a0c530bSNiels Provos memcpy(sop->event_writeset_out, sop->event_writeset_in,
1667a0c530bSNiels Provos sop->event_fdsz);
167aa6567feSNiels Provos
1686b22e74aSNick Mathewson nfds = sop->event_fds+1;
1696b22e74aSNick Mathewson
17076cd2b70SNick Mathewson EVBASE_RELEASE_LOCK(base, th_base_lock);
1716b22e74aSNick Mathewson
1726b22e74aSNick Mathewson res = select(nfds, sop->event_readset_out,
1737a0c530bSNiels Provos sop->event_writeset_out, NULL, tv);
174b855bc55SNiels Provos
17576cd2b70SNick Mathewson EVBASE_ACQUIRE_LOCK(base, th_base_lock);
1766b22e74aSNick Mathewson
1777a0c530bSNiels Provos check_selectop(sop);
178b855bc55SNiels Provos
179b855bc55SNiels Provos if (res == -1) {
180aa6567feSNiels Provos if (errno != EINTR) {
181fbdaf3abSNiels Provos event_warn("select");
182aa6567feSNiels Provos return (-1);
183aa6567feSNiels Provos }
184aa6567feSNiels Provos
185aa6567feSNiels Provos return (0);
18641b7cbc3SNiels Provos }
187aa6567feSNiels Provos
188fbdaf3abSNiels Provos event_debug(("%s: select reports %d", __func__, res));
189aa6567feSNiels Provos
1907a0c530bSNiels Provos check_selectop(sop);
191e86af4b7SNicholas Marriott i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
192d90149d4SNick Mathewson for (j = 0; j < nfds; ++j) {
193d90149d4SNick Mathewson if (++i >= nfds)
194cdaca02cSNick Mathewson i = 0;
195aa6567feSNiels Provos res = 0;
19602b2b4d1SNiels Provos if (FD_ISSET(i, sop->event_readset_out))
197aa6567feSNiels Provos res |= EV_READ;
19802b2b4d1SNiels Provos if (FD_ISSET(i, sop->event_writeset_out))
1997a0c530bSNiels Provos res |= EV_WRITE;
20002b2b4d1SNiels Provos
20102b2b4d1SNiels Provos if (res == 0)
20202b2b4d1SNiels Provos continue;
20302b2b4d1SNiels Provos
2048ac3c4c2SNick Mathewson evmap_io_active_(base, i, res);
2057a0c530bSNiels Provos }
2067a0c530bSNiels Provos check_selectop(sop);
207aa6567feSNiels Provos
208aa6567feSNiels Provos return (0);
209aa6567feSNiels Provos }
210aa6567feSNiels Provos
2117a0c530bSNiels Provos static int
select_resize(struct selectop * sop,int fdsz)2127a0c530bSNiels Provos select_resize(struct selectop *sop, int fdsz)
2137a0c530bSNiels Provos {
2147a0c530bSNiels Provos fd_set *readset_in = NULL;
2157a0c530bSNiels Provos fd_set *writeset_in = NULL;
2167a0c530bSNiels Provos
2177a0c530bSNiels Provos if (sop->event_readset_in)
2187a0c530bSNiels Provos check_selectop(sop);
2197a0c530bSNiels Provos
22049868b61SNick Mathewson if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
2217a0c530bSNiels Provos goto error;
2227a0c530bSNiels Provos sop->event_readset_in = readset_in;
223666b0966SJardel Weyrich if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
22483e805a4SNick Mathewson /* Note that this will leave event_readset_in expanded.
22583e805a4SNick Mathewson * That's okay; we wouldn't want to free it, since that would
22683e805a4SNick Mathewson * change the semantics of select_resize from "expand the
22783e805a4SNick Mathewson * readset_in and writeset_in, or return -1" to "expand the
22883e805a4SNick Mathewson * *set_in members, or trash them and return -1."
22983e805a4SNick Mathewson */
2307a0c530bSNiels Provos goto error;
231666b0966SJardel Weyrich }
2327a0c530bSNiels Provos sop->event_writeset_in = writeset_in;
2331eadb3e3SNick Mathewson sop->resize_out_sets = 1;
2347a0c530bSNiels Provos
2357a0c530bSNiels Provos memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
2367a0c530bSNiels Provos fdsz - sop->event_fdsz);
2377a0c530bSNiels Provos memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
2387a0c530bSNiels Provos fdsz - sop->event_fdsz);
2397a0c530bSNiels Provos
2407a0c530bSNiels Provos sop->event_fdsz = fdsz;
2417a0c530bSNiels Provos check_selectop(sop);
2427a0c530bSNiels Provos
2437a0c530bSNiels Provos return (0);
2447a0c530bSNiels Provos
2457a0c530bSNiels Provos error:
2467a0c530bSNiels Provos event_warn("malloc");
2477a0c530bSNiels Provos return (-1);
2487a0c530bSNiels Provos }
2497a0c530bSNiels Provos
2507a0c530bSNiels Provos
251ca42671aSNiels Provos static int
select_add(struct event_base * base,int fd,short old,short events,void * p)252554e1493SNick Mathewson select_add(struct event_base *base, int fd, short old, short events, void *p)
253aa6567feSNiels Provos {
25402b2b4d1SNiels Provos struct selectop *sop = base->evbase;
255554e1493SNick Mathewson (void) p;
256aa6567feSNiels Provos
2572e36dbe1SNick Mathewson EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
2587a0c530bSNiels Provos check_selectop(sop);
259aa6567feSNiels Provos /*
260aa6567feSNiels Provos * Keep track of the highest fd, so that we can calculate the size
261aa6567feSNiels Provos * of the fd_sets for select(2)
262aa6567feSNiels Provos */
26302b2b4d1SNiels Provos if (sop->event_fds < fd) {
2647a0c530bSNiels Provos int fdsz = sop->event_fdsz;
2657a0c530bSNiels Provos
2669c8db0f8SNick Mathewson if (fdsz < (int)sizeof(fd_mask))
2679c8db0f8SNick Mathewson fdsz = (int)sizeof(fd_mask);
2687a0c530bSNiels Provos
2699c8db0f8SNick Mathewson /* In theory we should worry about overflow here. In
2709c8db0f8SNick Mathewson * reality, though, the highest fd on a unixy system will
2719c8db0f8SNick Mathewson * not overflow here. XXXX */
27249d11362SNick Mathewson while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
2737a0c530bSNiels Provos fdsz *= 2;
2747a0c530bSNiels Provos
2757a0c530bSNiels Provos if (fdsz != sop->event_fdsz) {
2767a0c530bSNiels Provos if (select_resize(sop, fdsz)) {
2777a0c530bSNiels Provos check_selectop(sop);
2787a0c530bSNiels Provos return (-1);
2797a0c530bSNiels Provos }
2807a0c530bSNiels Provos }
2817a0c530bSNiels Provos
28202b2b4d1SNiels Provos sop->event_fds = fd;
2837a0c530bSNiels Provos }
2847a0c530bSNiels Provos
28502b2b4d1SNiels Provos if (events & EV_READ)
28602b2b4d1SNiels Provos FD_SET(fd, sop->event_readset_in);
28702b2b4d1SNiels Provos if (events & EV_WRITE)
28802b2b4d1SNiels Provos FD_SET(fd, sop->event_writeset_in);
2897a0c530bSNiels Provos check_selectop(sop);
290aa6567feSNiels Provos
291aa6567feSNiels Provos return (0);
292aa6567feSNiels Provos }
293aa6567feSNiels Provos
294aa6567feSNiels Provos /*
295aa6567feSNiels Provos * Nothing to be done here.
296aa6567feSNiels Provos */
297aa6567feSNiels Provos
298ca42671aSNiels Provos static int
select_del(struct event_base * base,int fd,short old,short events,void * p)299554e1493SNick Mathewson select_del(struct event_base *base, int fd, short old, short events, void *p)
300aa6567feSNiels Provos {
30102b2b4d1SNiels Provos struct selectop *sop = base->evbase;
302554e1493SNick Mathewson (void)p;
303b855bc55SNiels Provos
3042e36dbe1SNick Mathewson EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
3057a0c530bSNiels Provos check_selectop(sop);
3067a0c530bSNiels Provos
30702b2b4d1SNiels Provos if (sop->event_fds < fd) {
3087a0c530bSNiels Provos check_selectop(sop);
3097a0c530bSNiels Provos return (0);
3107a0c530bSNiels Provos }
3117a0c530bSNiels Provos
31202b2b4d1SNiels Provos if (events & EV_READ)
31302b2b4d1SNiels Provos FD_CLR(fd, sop->event_readset_in);
3147a0c530bSNiels Provos
31502b2b4d1SNiels Provos if (events & EV_WRITE)
31602b2b4d1SNiels Provos FD_CLR(fd, sop->event_writeset_in);
3177a0c530bSNiels Provos
3187a0c530bSNiels Provos check_selectop(sop);
3197a0c530bSNiels Provos return (0);
320b855bc55SNiels Provos }
3212e8051f5SNiels Provos
322ca42671aSNiels Provos static void
select_free_selectop(struct selectop * sop)3230c0ec0beSNick Mathewson select_free_selectop(struct selectop *sop)
3242e8051f5SNiels Provos {
3252e8051f5SNiels Provos if (sop->event_readset_in)
32649868b61SNick Mathewson mm_free(sop->event_readset_in);
3272e8051f5SNiels Provos if (sop->event_writeset_in)
32849868b61SNick Mathewson mm_free(sop->event_writeset_in);
3292e8051f5SNiels Provos if (sop->event_readset_out)
33049868b61SNick Mathewson mm_free(sop->event_readset_out);
3312e8051f5SNiels Provos if (sop->event_writeset_out)
33249868b61SNick Mathewson mm_free(sop->event_writeset_out);
3332e8051f5SNiels Provos
3342e8051f5SNiels Provos memset(sop, 0, sizeof(struct selectop));
33549868b61SNick Mathewson mm_free(sop);
3362e8051f5SNiels Provos }
3370c0ec0beSNick Mathewson
3380c0ec0beSNick Mathewson static void
select_dealloc(struct event_base * base)3390c0ec0beSNick Mathewson select_dealloc(struct event_base *base)
3400c0ec0beSNick Mathewson {
3418ac3c4c2SNick Mathewson evsig_dealloc_(base);
3420c0ec0beSNick Mathewson
3430c0ec0beSNick Mathewson select_free_selectop(base->evbase);
3440c0ec0beSNick Mathewson }
34576d4c929SRoss Lagerwall
34668120d9bSNick Mathewson #endif /* EVENT__HAVE_SELECT */
347