1/*	$OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $	*/
2
3/*
4 * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <sys/types.h>
34#ifdef HAVE_SYS_TIME_H
35#include <sys/time.h>
36#else
37#include <sys/_libevent_time.h>
38#endif
39#ifdef HAVE_SYS_SELECT_H
40#include <sys/select.h>
41#endif
42#include <sys/queue.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <errno.h>
49#ifdef CHECK_INVARIANTS
50#include <assert.h>
51#endif
52
53#include "event.h"
54#include "evutil.h"
55#include "event-internal.h"
56#include "evsignal.h"
57#include "log.h"
58
59#ifndef howmany
60#define        howmany(x, y)   (((x)+((y)-1))/(y))
61#endif
62
63#ifndef _EVENT_HAVE_FD_MASK
64/* This type is mandatory, but Android doesn't define it. */
65#undef NFDBITS
66#define NFDBITS (sizeof(long)*8)
67typedef unsigned long fd_mask;
68#endif
69
70struct selectop {
71	int event_fds;		/* Highest fd in fd set */
72	int event_fdsz;
73	fd_set *event_readset_in;
74	fd_set *event_writeset_in;
75	fd_set *event_readset_out;
76	fd_set *event_writeset_out;
77	struct event **event_r_by_fd;
78	struct event **event_w_by_fd;
79};
80
81static void *select_init	(struct event_base *);
82static int select_add		(void *, struct event *);
83static int select_del		(void *, struct event *);
84static int select_dispatch	(struct event_base *, void *, struct timeval *);
85static void select_dealloc     (struct event_base *, void *);
86
87const struct eventop selectops = {
88	"select",
89	select_init,
90	select_add,
91	select_del,
92	select_dispatch,
93	select_dealloc,
94	0
95};
96
97static int select_resize(struct selectop *sop, int fdsz);
98
99static void *
100select_init(struct event_base *base)
101{
102	struct selectop *sop;
103
104	/* Disable select when this environment variable is set */
105	if (evutil_getenv("EVENT_NOSELECT"))
106		return (NULL);
107
108	if (!(sop = calloc(1, sizeof(struct selectop))))
109		return (NULL);
110
111	select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask));
112
113	evsignal_init(base);
114
115	return (sop);
116}
117
118#ifdef CHECK_INVARIANTS
119static void
120check_selectop(struct selectop *sop)
121{
122	int i;
123	for (i = 0; i <= sop->event_fds; ++i) {
124		if (FD_ISSET(i, sop->event_readset_in)) {
125			assert(sop->event_r_by_fd[i]);
126			assert(sop->event_r_by_fd[i]->ev_events & EV_READ);
127			assert(sop->event_r_by_fd[i]->ev_fd == i);
128		} else {
129			assert(! sop->event_r_by_fd[i]);
130		}
131		if (FD_ISSET(i, sop->event_writeset_in)) {
132			assert(sop->event_w_by_fd[i]);
133			assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE);
134			assert(sop->event_w_by_fd[i]->ev_fd == i);
135		} else {
136			assert(! sop->event_w_by_fd[i]);
137		}
138	}
139
140}
141#else
142#define check_selectop(sop) do { (void) sop; } while (0)
143#endif
144
145static int
146select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
147{
148	int res, i, j;
149	struct selectop *sop = arg;
150
151	check_selectop(sop);
152
153	memcpy(sop->event_readset_out, sop->event_readset_in,
154	       sop->event_fdsz);
155	memcpy(sop->event_writeset_out, sop->event_writeset_in,
156	       sop->event_fdsz);
157
158	res = select(sop->event_fds + 1, sop->event_readset_out,
159	    sop->event_writeset_out, NULL, tv);
160
161	check_selectop(sop);
162
163	if (res == -1) {
164		if (errno != EINTR) {
165			event_warn("select");
166			return (-1);
167		}
168
169		evsignal_process(base);
170		return (0);
171	} else if (base->sig.evsignal_caught) {
172		evsignal_process(base);
173	}
174
175	event_debug(("%s: select reports %d", __func__, res));
176
177	check_selectop(sop);
178	i = random() % (sop->event_fds+1);
179	for (j = 0; j <= sop->event_fds; ++j) {
180		struct event *r_ev = NULL, *w_ev = NULL;
181		if (++i >= sop->event_fds+1)
182			i = 0;
183
184		res = 0;
185		if (FD_ISSET(i, sop->event_readset_out)) {
186			r_ev = sop->event_r_by_fd[i];
187			res |= EV_READ;
188		}
189		if (FD_ISSET(i, sop->event_writeset_out)) {
190			w_ev = sop->event_w_by_fd[i];
191			res |= EV_WRITE;
192		}
193		if (r_ev && (res & r_ev->ev_events)) {
194			event_active(r_ev, res & r_ev->ev_events, 1);
195		}
196		if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
197			event_active(w_ev, res & w_ev->ev_events, 1);
198		}
199	}
200	check_selectop(sop);
201
202	return (0);
203}
204
205
206static int
207select_resize(struct selectop *sop, int fdsz)
208{
209	int n_events, n_events_old;
210
211	fd_set *readset_in = NULL;
212	fd_set *writeset_in = NULL;
213	fd_set *readset_out = NULL;
214	fd_set *writeset_out = NULL;
215	struct event **r_by_fd = NULL;
216	struct event **w_by_fd = NULL;
217
218	n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
219	n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
220
221	if (sop->event_readset_in)
222		check_selectop(sop);
223
224	if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
225		goto error;
226	sop->event_readset_in = readset_in;
227	if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
228		goto error;
229	sop->event_readset_out = readset_out;
230	if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
231		goto error;
232	sop->event_writeset_in = writeset_in;
233	if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
234		goto error;
235	sop->event_writeset_out = writeset_out;
236	if ((r_by_fd = realloc(sop->event_r_by_fd,
237		 n_events*sizeof(struct event*))) == NULL)
238		goto error;
239	sop->event_r_by_fd = r_by_fd;
240	if ((w_by_fd = realloc(sop->event_w_by_fd,
241		 n_events * sizeof(struct event*))) == NULL)
242		goto error;
243	sop->event_w_by_fd = w_by_fd;
244
245	memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
246	    fdsz - sop->event_fdsz);
247	memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
248	    fdsz - sop->event_fdsz);
249	memset(sop->event_r_by_fd + n_events_old, 0,
250	    (n_events-n_events_old) * sizeof(struct event*));
251	memset(sop->event_w_by_fd + n_events_old, 0,
252	    (n_events-n_events_old) * sizeof(struct event*));
253
254	sop->event_fdsz = fdsz;
255	check_selectop(sop);
256
257	return (0);
258
259 error:
260	event_warn("malloc");
261	return (-1);
262}
263
264
265static int
266select_add(void *arg, struct event *ev)
267{
268	struct selectop *sop = arg;
269
270	if (ev->ev_events & EV_SIGNAL)
271		return (evsignal_add(ev));
272
273	check_selectop(sop);
274	/*
275	 * Keep track of the highest fd, so that we can calculate the size
276	 * of the fd_sets for select(2)
277	 */
278	if (sop->event_fds < ev->ev_fd) {
279		int fdsz = sop->event_fdsz;
280
281		if (fdsz < sizeof(fd_mask))
282			fdsz = sizeof(fd_mask);
283
284		while (fdsz <
285		    (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
286			fdsz *= 2;
287
288		if (fdsz != sop->event_fdsz) {
289			if (select_resize(sop, fdsz)) {
290				check_selectop(sop);
291				return (-1);
292			}
293		}
294
295		sop->event_fds = ev->ev_fd;
296	}
297
298	if (ev->ev_events & EV_READ) {
299		FD_SET(ev->ev_fd, sop->event_readset_in);
300		sop->event_r_by_fd[ev->ev_fd] = ev;
301	}
302	if (ev->ev_events & EV_WRITE) {
303		FD_SET(ev->ev_fd, sop->event_writeset_in);
304		sop->event_w_by_fd[ev->ev_fd] = ev;
305	}
306	check_selectop(sop);
307
308	return (0);
309}
310
311/*
312 * Nothing to be done here.
313 */
314
315static int
316select_del(void *arg, struct event *ev)
317{
318	struct selectop *sop = arg;
319
320	check_selectop(sop);
321	if (ev->ev_events & EV_SIGNAL)
322		return (evsignal_del(ev));
323
324	if (sop->event_fds < ev->ev_fd) {
325		check_selectop(sop);
326		return (0);
327	}
328
329	if (ev->ev_events & EV_READ) {
330		FD_CLR(ev->ev_fd, sop->event_readset_in);
331		sop->event_r_by_fd[ev->ev_fd] = NULL;
332	}
333
334	if (ev->ev_events & EV_WRITE) {
335		FD_CLR(ev->ev_fd, sop->event_writeset_in);
336		sop->event_w_by_fd[ev->ev_fd] = NULL;
337	}
338
339	check_selectop(sop);
340	return (0);
341}
342
343static void
344select_dealloc(struct event_base *base, void *arg)
345{
346	struct selectop *sop = arg;
347
348	evsignal_dealloc(base);
349	if (sop->event_readset_in)
350		free(sop->event_readset_in);
351	if (sop->event_writeset_in)
352		free(sop->event_writeset_in);
353	if (sop->event_readset_out)
354		free(sop->event_readset_out);
355	if (sop->event_writeset_out)
356		free(sop->event_writeset_out);
357	if (sop->event_r_by_fd)
358		free(sop->event_r_by_fd);
359	if (sop->event_w_by_fd)
360		free(sop->event_w_by_fd);
361
362	memset(sop, 0, sizeof(struct selectop));
363	free(sop);
364}
365