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/_time.h>
38#endif
39#include <sys/queue.h>
40#include <sys/tree.h>
41#include <signal.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include <errno.h>
47#ifdef CHECK_INVARIANTS
48#include <assert.h>
49#endif
50
51#include "event.h"
52#include "event-internal.h"
53#include "evsignal.h"
54#include "log.h"
55
56#ifndef howmany
57#define        howmany(x, y)   (((x)+((y)-1))/(y))
58#endif
59
60extern volatile sig_atomic_t evsignal_caught;
61
62struct selectop {
63	int event_fds;		/* Highest fd in fd set */
64	int event_fdsz;
65	fd_set *event_readset_in;
66	fd_set *event_writeset_in;
67	fd_set *event_readset_out;
68	fd_set *event_writeset_out;
69	struct event **event_r_by_fd;
70	struct event **event_w_by_fd;
71};
72
73void *select_init	(void);
74int select_add		(void *, struct event *);
75int select_del		(void *, struct event *);
76int select_recalc	(struct event_base *, void *, int);
77int select_dispatch	(struct event_base *, void *, struct timeval *);
78void select_dealloc     (void *);
79
80const struct eventop selectops = {
81	"select",
82	select_init,
83	select_add,
84	select_del,
85	select_recalc,
86	select_dispatch,
87	select_dealloc
88};
89
90static int select_resize(struct selectop *sop, int fdsz);
91
92void *
93select_init(void)
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();
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
138/*
139 * Called with the highest fd that we know about.  If it is 0, completely
140 * recalculate everything.
141 */
142
143int
144select_recalc(struct event_base *base, void *arg, int max)
145{
146	struct selectop *sop = arg;
147
148	check_selectop(sop);
149
150	return (0);
151}
152
153int
154select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
155{
156	int res, i;
157	struct selectop *sop = arg;
158
159	check_selectop(sop);
160
161	memcpy(sop->event_readset_out, sop->event_readset_in,
162	       sop->event_fdsz);
163	memcpy(sop->event_writeset_out, sop->event_writeset_in,
164	       sop->event_fdsz);
165
166	res = select(sop->event_fds + 1, sop->event_readset_out,
167	    sop->event_writeset_out, NULL, tv);
168
169	check_selectop(sop);
170
171	if (res == -1) {
172		if (errno != EINTR) {
173			event_warn("select");
174			return (-1);
175		}
176
177		evsignal_process();
178		return (0);
179	} else if (evsignal_caught)
180		evsignal_process();
181
182	event_debug(("%s: select reports %d", __func__, res));
183
184	check_selectop(sop);
185	for (i = 0; i <= sop->event_fds; ++i) {
186		struct event *r_ev = NULL, *w_ev = NULL;
187		res = 0;
188		if (FD_ISSET(i, sop->event_readset_out)) {
189			r_ev = sop->event_r_by_fd[i];
190			res |= EV_READ;
191		}
192		if (FD_ISSET(i, sop->event_writeset_out)) {
193			w_ev = sop->event_w_by_fd[i];
194			res |= EV_WRITE;
195		}
196		if (r_ev && (res & r_ev->ev_events)) {
197			if (!(r_ev->ev_events & EV_PERSIST))
198				event_del(r_ev);
199			event_active(r_ev, res & r_ev->ev_events, 1);
200		}
201		if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
202			if (!(w_ev->ev_events & EV_PERSIST))
203				event_del(w_ev);
204			event_active(w_ev, res & w_ev->ev_events, 1);
205		}
206	}
207	check_selectop(sop);
208
209	return (0);
210}
211
212
213static int
214select_resize(struct selectop *sop, int fdsz)
215{
216	int n_events, n_events_old;
217
218	fd_set *readset_in = NULL;
219	fd_set *writeset_in = NULL;
220	fd_set *readset_out = NULL;
221	fd_set *writeset_out = NULL;
222	struct event **r_by_fd = NULL;
223	struct event **w_by_fd = NULL;
224
225	n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
226	n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
227
228	if (sop->event_readset_in)
229		check_selectop(sop);
230
231	if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
232		goto error;
233	sop->event_readset_in = readset_in;
234	if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
235		goto error;
236	sop->event_readset_out = readset_out;
237	if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
238		goto error;
239	sop->event_writeset_in = writeset_in;
240	if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
241		goto error;
242	sop->event_writeset_out = writeset_out;
243	if ((r_by_fd = realloc(sop->event_r_by_fd,
244		 n_events*sizeof(struct event*))) == NULL)
245		goto error;
246	sop->event_r_by_fd = r_by_fd;
247	if ((w_by_fd = realloc(sop->event_w_by_fd,
248		 n_events * sizeof(struct event*))) == NULL)
249		goto error;
250	sop->event_w_by_fd = w_by_fd;
251
252	memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
253	    fdsz - sop->event_fdsz);
254	memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
255	    fdsz - sop->event_fdsz);
256	memset(sop->event_r_by_fd + n_events_old, 0,
257	    (n_events-n_events_old) * sizeof(struct event*));
258	memset(sop->event_w_by_fd + n_events_old, 0,
259	    (n_events-n_events_old) * sizeof(struct event*));
260
261	sop->event_fdsz = fdsz;
262	check_selectop(sop);
263
264	return (0);
265
266 error:
267	event_warn("malloc");
268	return (-1);
269}
270
271
272int
273select_add(void *arg, struct event *ev)
274{
275	struct selectop *sop = arg;
276
277	if (ev->ev_events & EV_SIGNAL)
278		return (evsignal_add(ev));
279
280	check_selectop(sop);
281	/*
282	 * Keep track of the highest fd, so that we can calculate the size
283	 * of the fd_sets for select(2)
284	 */
285	if (sop->event_fds < ev->ev_fd) {
286		int fdsz = sop->event_fdsz;
287
288		if (fdsz < sizeof(fd_mask))
289			fdsz = sizeof(fd_mask);
290
291		while (fdsz <
292		    (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
293			fdsz *= 2;
294
295		if (fdsz != sop->event_fdsz) {
296			if (select_resize(sop, fdsz)) {
297				check_selectop(sop);
298				return (-1);
299			}
300		}
301
302		sop->event_fds = ev->ev_fd;
303	}
304
305	if (ev->ev_events & EV_READ) {
306		FD_SET(ev->ev_fd, sop->event_readset_in);
307		sop->event_r_by_fd[ev->ev_fd] = ev;
308	}
309	if (ev->ev_events & EV_WRITE) {
310		FD_SET(ev->ev_fd, sop->event_writeset_in);
311		sop->event_w_by_fd[ev->ev_fd] = ev;
312	}
313	check_selectop(sop);
314
315	return (0);
316}
317
318/*
319 * Nothing to be done here.
320 */
321
322int
323select_del(void *arg, struct event *ev)
324{
325	struct selectop *sop = arg;
326
327	check_selectop(sop);
328	if (ev->ev_events & EV_SIGNAL)
329		return (evsignal_del(ev));
330
331	if (sop->event_fds < ev->ev_fd) {
332		check_selectop(sop);
333		return (0);
334	}
335
336	if (ev->ev_events & EV_READ) {
337		FD_CLR(ev->ev_fd, sop->event_readset_in);
338		sop->event_r_by_fd[ev->ev_fd] = NULL;
339	}
340
341	if (ev->ev_events & EV_WRITE) {
342		FD_CLR(ev->ev_fd, sop->event_writeset_in);
343		sop->event_w_by_fd[ev->ev_fd] = NULL;
344	}
345
346	check_selectop(sop);
347	return (0);
348}
349
350void
351select_dealloc(void *arg)
352{
353	struct selectop *sop = arg;
354
355	if (sop->event_readset_in)
356		free(sop->event_readset_in);
357	if (sop->event_writeset_in)
358		free(sop->event_writeset_in);
359	if (sop->event_readset_out)
360		free(sop->event_readset_out);
361	if (sop->event_writeset_out)
362		free(sop->event_writeset_out);
363	if (sop->event_r_by_fd)
364		free(sop->event_r_by_fd);
365	if (sop->event_w_by_fd)
366		free(sop->event_w_by_fd);
367
368	memset(sop, 0, sizeof(struct selectop));
369	free(sop);
370}
371