mini_event.c revision 356345
1/*
2 * mini_event.c - implementation of part of libevent api, portably.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
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 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37/**
38 * \file
39 * fake libevent implementation. Less broad in functionality, and only
40 * supports select(2).
41 */
42
43#include "config.h"
44#include "util/mini_event.h"
45#ifdef HAVE_TIME_H
46#include <time.h>
47#endif
48#include <sys/time.h>
49
50#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
51#include <signal.h>
52#include "util/fptr_wlist.h"
53
54/** compare events in tree, based on timevalue, ptr for uniqueness */
55int mini_ev_cmp(const void* a, const void* b)
56{
57	const struct event *e = (const struct event*)a;
58	const struct event *f = (const struct event*)b;
59	if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
60		return -1;
61	if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
62		return 1;
63	if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
64		return -1;
65	if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
66		return 1;
67	if(e < f)
68		return -1;
69	if(e > f)
70		return 1;
71	return 0;
72}
73
74/** set time */
75static int
76settime(struct event_base* base)
77{
78	if(gettimeofday(base->time_tv, NULL) < 0) {
79		return -1;
80	}
81#ifndef S_SPLINT_S
82	*base->time_secs = (time_t)base->time_tv->tv_sec;
83#endif
84	return 0;
85}
86
87/** create event base */
88void *event_init(time_t* time_secs, struct timeval* time_tv)
89{
90	struct event_base* base = (struct event_base*)malloc(
91		sizeof(struct event_base));
92	if(!base)
93		return NULL;
94	memset(base, 0, sizeof(*base));
95	base->time_secs = time_secs;
96	base->time_tv = time_tv;
97	if(settime(base) < 0) {
98		event_base_free(base);
99		return NULL;
100	}
101	base->times = rbtree_create(mini_ev_cmp);
102	if(!base->times) {
103		event_base_free(base);
104		return NULL;
105	}
106	base->capfd = MAX_FDS;
107#ifdef FD_SETSIZE
108	if((int)FD_SETSIZE < base->capfd)
109		base->capfd = (int)FD_SETSIZE;
110#endif
111	base->fds = (struct event**)calloc((size_t)base->capfd,
112		sizeof(struct event*));
113	if(!base->fds) {
114		event_base_free(base);
115		return NULL;
116	}
117	base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
118	if(!base->signals) {
119		event_base_free(base);
120		return NULL;
121	}
122#ifndef S_SPLINT_S
123	FD_ZERO(&base->reads);
124	FD_ZERO(&base->writes);
125#endif
126	return base;
127}
128
129/** get version */
130const char *event_get_version(void)
131{
132	return "mini-event-"PACKAGE_VERSION;
133}
134
135/** get polling method, select */
136const char *event_get_method(void)
137{
138	return "select";
139}
140
141/** call timeouts handlers, and return how long to wait for next one or -1 */
142static void handle_timeouts(struct event_base* base, struct timeval* now,
143	struct timeval* wait)
144{
145	struct event* p;
146#ifndef S_SPLINT_S
147	wait->tv_sec = (time_t)-1;
148#endif
149
150	while((rbnode_type*)(p = (struct event*)rbtree_first(base->times))
151		!=RBTREE_NULL) {
152#ifndef S_SPLINT_S
153		if(p->ev_timeout.tv_sec > now->tv_sec ||
154			(p->ev_timeout.tv_sec==now->tv_sec &&
155		 	p->ev_timeout.tv_usec > now->tv_usec)) {
156			/* there is a next larger timeout. wait for it */
157			wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
158			if(now->tv_usec > p->ev_timeout.tv_usec) {
159				wait->tv_sec--;
160				wait->tv_usec = 1000000 - (now->tv_usec -
161					p->ev_timeout.tv_usec);
162			} else {
163				wait->tv_usec = p->ev_timeout.tv_usec
164					- now->tv_usec;
165			}
166			return;
167		}
168#endif
169		/* event times out, remove it */
170		(void)rbtree_delete(base->times, p);
171		p->ev_events &= ~EV_TIMEOUT;
172		fptr_ok(fptr_whitelist_event(p->ev_callback));
173		(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
174	}
175}
176
177/** call select and callbacks for that */
178static int handle_select(struct event_base* base, struct timeval* wait)
179{
180	fd_set r, w;
181	int ret, i;
182
183#ifndef S_SPLINT_S
184	if(wait->tv_sec==(time_t)-1)
185		wait = NULL;
186#endif
187	memmove(&r, &base->reads, sizeof(fd_set));
188	memmove(&w, &base->writes, sizeof(fd_set));
189	memmove(&base->ready, &base->content, sizeof(fd_set));
190
191	if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
192		ret = errno;
193		if(settime(base) < 0)
194			return -1;
195		errno = ret;
196		if(ret == EAGAIN || ret == EINTR)
197			return 0;
198		return -1;
199	}
200	if(settime(base) < 0)
201		return -1;
202
203	for(i=0; i<base->maxfd+1; i++) {
204		short bits = 0;
205		if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
206			continue;
207		}
208		if(FD_ISSET(i, &r)) {
209			bits |= EV_READ;
210			ret--;
211		}
212		if(FD_ISSET(i, &w)) {
213			bits |= EV_WRITE;
214			ret--;
215		}
216		bits &= base->fds[i]->ev_events;
217		if(bits) {
218			fptr_ok(fptr_whitelist_event(
219				base->fds[i]->ev_callback));
220			(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
221				bits, base->fds[i]->ev_arg);
222			if(ret==0)
223				break;
224		}
225	}
226	return 0;
227}
228
229/** run select in a loop */
230int event_base_dispatch(struct event_base* base)
231{
232	struct timeval wait;
233	if(settime(base) < 0)
234		return -1;
235	while(!base->need_to_exit)
236	{
237		/* see if timeouts need handling */
238		handle_timeouts(base, base->time_tv, &wait);
239		if(base->need_to_exit)
240			return 0;
241		/* do select */
242		if(handle_select(base, &wait) < 0) {
243			if(base->need_to_exit)
244				return 0;
245			return -1;
246		}
247	}
248	return 0;
249}
250
251/** exit that loop */
252int event_base_loopexit(struct event_base* base,
253	struct timeval* ATTR_UNUSED(tv))
254{
255	base->need_to_exit = 1;
256	return 0;
257}
258
259/* free event base, free events yourself */
260void event_base_free(struct event_base* base)
261{
262	if(!base)
263		return;
264	free(base->times);
265	free(base->fds);
266	free(base->signals);
267	free(base);
268}
269
270/** set content of event */
271void event_set(struct event* ev, int fd, short bits,
272	void (*cb)(int, short, void *), void* arg)
273{
274	ev->node.key = ev;
275	ev->ev_fd = fd;
276	ev->ev_events = bits;
277	ev->ev_callback = cb;
278	fptr_ok(fptr_whitelist_event(ev->ev_callback));
279	ev->ev_arg = arg;
280	ev->added = 0;
281}
282
283/* add event to a base */
284int event_base_set(struct event_base* base, struct event* ev)
285{
286	ev->ev_base = base;
287	ev->added = 0;
288	return 0;
289}
290
291/* add event to make it active, you may not change it with event_set anymore */
292int event_add(struct event* ev, struct timeval* tv)
293{
294	if(ev->added)
295		event_del(ev);
296	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
297		return -1;
298	if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
299		ev->ev_base->fds[ev->ev_fd] = ev;
300		if(ev->ev_events&EV_READ) {
301			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
302		}
303		if(ev->ev_events&EV_WRITE) {
304			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
305		}
306		FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
307		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
308		if(ev->ev_fd > ev->ev_base->maxfd)
309			ev->ev_base->maxfd = ev->ev_fd;
310	}
311	if(tv && (ev->ev_events&EV_TIMEOUT)) {
312#ifndef S_SPLINT_S
313		struct timeval *now = ev->ev_base->time_tv;
314		ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
315		ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
316		while(ev->ev_timeout.tv_usec >= 1000000) {
317			ev->ev_timeout.tv_usec -= 1000000;
318			ev->ev_timeout.tv_sec++;
319		}
320#endif
321		(void)rbtree_insert(ev->ev_base->times, &ev->node);
322	}
323	ev->added = 1;
324	return 0;
325}
326
327/* remove event, you may change it again */
328int event_del(struct event* ev)
329{
330	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
331		return -1;
332	if((ev->ev_events&EV_TIMEOUT))
333		(void)rbtree_delete(ev->ev_base->times, &ev->node);
334	if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
335		ev->ev_base->fds[ev->ev_fd] = NULL;
336		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
337		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
338		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
339		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
340	}
341	ev->added = 0;
342	return 0;
343}
344
345/** which base gets to handle signals */
346static struct event_base* signal_base = NULL;
347/** signal handler */
348static RETSIGTYPE sigh(int sig)
349{
350	struct event* ev;
351	if(!signal_base || sig < 0 || sig >= MAX_SIG)
352		return;
353	ev = signal_base->signals[sig];
354	if(!ev)
355		return;
356	fptr_ok(fptr_whitelist_event(ev->ev_callback));
357	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
358}
359
360/** install signal handler */
361int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
362{
363	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
364		return -1;
365	signal_base = ev->ev_base;
366	ev->ev_base->signals[ev->ev_fd] = ev;
367	ev->added = 1;
368	if(signal(ev->ev_fd, sigh) == SIG_ERR) {
369		return -1;
370	}
371	return 0;
372}
373
374/** remove signal handler */
375int signal_del(struct event* ev)
376{
377	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
378		return -1;
379	ev->ev_base->signals[ev->ev_fd] = NULL;
380	ev->added = 0;
381	return 0;
382}
383
384#else /* USE_MINI_EVENT */
385#ifndef USE_WINSOCK
386int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
387{
388	return 0;
389}
390#endif /* not USE_WINSOCK */
391#endif /* USE_MINI_EVENT */
392