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