diff options
Diffstat (limited to 'util/mini_event.c')
| -rw-r--r-- | util/mini_event.c | 394 | 
1 files changed, 394 insertions, 0 deletions
| diff --git a/util/mini_event.c b/util/mini_event.c new file mode 100644 index 000000000000..f66214ddb8fa --- /dev/null +++ b/util/mini_event.c @@ -0,0 +1,394 @@ +/* + * mini_event.c - implementation of part of libevent api, portably. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + *  + * This software is open source. + *  + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + *  + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + *  + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + *  + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *  + */ + +/** + * \file + * fake libevent implementation. Less broad in functionality, and only + * supports select(2). + */ + +#include "config.h" +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include <sys/time.h> + +#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK) +#include <signal.h> +#include "util/mini_event.h" +#include "util/fptr_wlist.h" + +/** compare events in tree, based on timevalue, ptr for uniqueness */ +int mini_ev_cmp(const void* a, const void* b) +{ +	const struct event *e = (const struct event*)a; +	const struct event *f = (const struct event*)b; +	if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec) +		return -1; +	if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec) +		return 1; +	if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec) +		return -1; +	if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec) +		return 1; +	if(e < f) +		return -1; +	if(e > f) +		return 1; +	return 0; +} + +/** set time */ +static int +settime(struct event_base* base) +{ +	if(gettimeofday(base->time_tv, NULL) < 0) { +		return -1; +	} +#ifndef S_SPLINT_S +	*base->time_secs = (uint32_t)base->time_tv->tv_sec; +#endif +	return 0; +} + +/** create event base */ +void *event_init(uint32_t* time_secs, struct timeval* time_tv) +{ +	struct event_base* base = (struct event_base*)malloc( +		sizeof(struct event_base)); +	if(!base) +		return NULL; +	memset(base, 0, sizeof(*base)); +	base->time_secs = time_secs; +	base->time_tv = time_tv; +	if(settime(base) < 0) { +		event_base_free(base); +		return NULL; +	} +	base->times = rbtree_create(mini_ev_cmp); +	if(!base->times) { +		event_base_free(base); +		return NULL; +	} +	base->capfd = MAX_FDS; +#ifdef FD_SETSIZE +	if((int)FD_SETSIZE < base->capfd) +		base->capfd = (int)FD_SETSIZE; +#endif +	base->fds = (struct event**)calloc((size_t)base->capfd,  +		sizeof(struct event*)); +	if(!base->fds) { +		event_base_free(base); +		return NULL; +	} +	base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*)); +	if(!base->signals) { +		event_base_free(base); +		return NULL; +	} +#ifndef S_SPLINT_S +	FD_ZERO(&base->reads); +	FD_ZERO(&base->writes); +#endif +	return base; +} + +/** get version */ +const char *event_get_version(void) +{ +	return "mini-event-"PACKAGE_VERSION; +} + +/** get polling method, select */ +const char *event_get_method(void) +{ +	return "select"; +} + +/** call timeouts handlers, and return how long to wait for next one or -1 */ +static void handle_timeouts(struct event_base* base, struct timeval* now,  +	struct timeval* wait) +{ +	struct event* p; +#ifndef S_SPLINT_S +	wait->tv_sec = (time_t)-1; +#endif + +	while((rbnode_t*)(p = (struct event*)rbtree_first(base->times)) +		!=RBTREE_NULL) { +#ifndef S_SPLINT_S +		if(p->ev_timeout.tv_sec > now->tv_sec || +			(p->ev_timeout.tv_sec==now->tv_sec &&  +		 	p->ev_timeout.tv_usec > now->tv_usec)) { +			/* there is a next larger timeout. wait for it */ +			wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec; +			if(now->tv_usec > p->ev_timeout.tv_usec) { +				wait->tv_sec--; +				wait->tv_usec = 1000000 - (now->tv_usec - +					p->ev_timeout.tv_usec); +			} else { +				wait->tv_usec = p->ev_timeout.tv_usec  +					- now->tv_usec; +			} +			return; +		} +#endif +		/* event times out, remove it */ +		(void)rbtree_delete(base->times, p); +		p->ev_events &= ~EV_TIMEOUT; +		fptr_ok(fptr_whitelist_event(p->ev_callback)); +		(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg); +	} +} + +/** call select and callbacks for that */ +static int handle_select(struct event_base* base, struct timeval* wait) +{ +	fd_set r, w; +	int ret, i; + +#ifndef S_SPLINT_S +	if(wait->tv_sec==(time_t)-1) +		wait = NULL; +#endif +	memmove(&r, &base->reads, sizeof(fd_set)); +	memmove(&w, &base->writes, sizeof(fd_set)); +	memmove(&base->ready, &base->content, sizeof(fd_set)); + +	if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) { +		ret = errno; +		if(settime(base) < 0) +			return -1; +		errno = ret; +		if(ret == EAGAIN || ret == EINTR) +			return 0; +		return -1; +	} +	if(settime(base) < 0) +		return -1; +	 +	for(i=0; i<base->maxfd+1; i++) { +		short bits = 0; +		if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) { +			continue; +		} +		if(FD_ISSET(i, &r)) { +			bits |= EV_READ; +			ret--; +		} +		if(FD_ISSET(i, &w)) { +			bits |= EV_WRITE; +			ret--; +		} +		bits &= base->fds[i]->ev_events; +		if(bits) { +			fptr_ok(fptr_whitelist_event( +				base->fds[i]->ev_callback)); +			(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,  +				bits, base->fds[i]->ev_arg); +			if(ret==0) +				break; +		} +	} +	return 0; +} + +/** run select in a loop */ +int event_base_dispatch(struct event_base* base) +{ +	struct timeval wait; +	if(settime(base) < 0) +		return -1; +	while(!base->need_to_exit) +	{ +		/* see if timeouts need handling */ +		handle_timeouts(base, base->time_tv, &wait); +		if(base->need_to_exit) +			return 0; +		/* do select */ +		if(handle_select(base, &wait) < 0) { +			if(base->need_to_exit) +				return 0; +			return -1; +		} +	} +	return 0; +} + +/** exit that loop */ +int event_base_loopexit(struct event_base* base,  +	struct timeval* ATTR_UNUSED(tv)) +{ +	base->need_to_exit = 1; +	return 0; +} + +/* free event base, free events yourself */ +void event_base_free(struct event_base* base) +{ +	if(!base) +		return; +	if(base->times) +		free(base->times); +	if(base->fds) +		free(base->fds); +	if(base->signals) +		free(base->signals); +	free(base); +} + +/** set content of event */ +void event_set(struct event* ev, int fd, short bits,  +	void (*cb)(int, short, void *), void* arg) +{ +	ev->node.key = ev; +	ev->ev_fd = fd; +	ev->ev_events = bits; +	ev->ev_callback = cb; +	fptr_ok(fptr_whitelist_event(ev->ev_callback)); +	ev->ev_arg = arg; +	ev->added = 0; +} + +/* add event to a base */ +int event_base_set(struct event_base* base, struct event* ev) +{ +	ev->ev_base = base; +	ev->added = 0; +	return 0; +} + +/* add event to make it active, you may not change it with event_set anymore */ +int event_add(struct event* ev, struct timeval* tv) +{ +	if(ev->added) +		event_del(ev); +	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd) +		return -1; +	if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { +		ev->ev_base->fds[ev->ev_fd] = ev; +		if(ev->ev_events&EV_READ) { +			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads); +		} +		if(ev->ev_events&EV_WRITE) { +			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes); +		} +		FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content); +		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready); +		if(ev->ev_fd > ev->ev_base->maxfd) +			ev->ev_base->maxfd = ev->ev_fd; +	} +	if(tv && (ev->ev_events&EV_TIMEOUT)) { +#ifndef S_SPLINT_S +		struct timeval *now = ev->ev_base->time_tv; +		ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; +		ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; +		while(ev->ev_timeout.tv_usec > 1000000) { +			ev->ev_timeout.tv_usec -= 1000000; +			ev->ev_timeout.tv_sec++; +		} +#endif +		(void)rbtree_insert(ev->ev_base->times, &ev->node); +	} +	ev->added = 1; +	return 0; +} + +/* remove event, you may change it again */ +int event_del(struct event* ev) +{ +	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd) +		return -1; +	if((ev->ev_events&EV_TIMEOUT)) +		(void)rbtree_delete(ev->ev_base->times, &ev->node); +	if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) { +		ev->ev_base->fds[ev->ev_fd] = NULL; +		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads); +		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes); +		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready); +		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content); +	} +	ev->added = 0; +	return 0; +} + +/** which base gets to handle signals */ +static struct event_base* signal_base = NULL; +/** signal handler */ +static RETSIGTYPE sigh(int sig) +{ +	struct event* ev; +	if(!signal_base || sig < 0 || sig >= MAX_SIG) +		return; +	ev = signal_base->signals[sig]; +	if(!ev) +		return; +	fptr_ok(fptr_whitelist_event(ev->ev_callback)); +	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg); +} + +/** install signal handler */ +int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv)) +{ +	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) +		return -1; +	signal_base = ev->ev_base; +	ev->ev_base->signals[ev->ev_fd] = ev; +	ev->added = 1; +	if(signal(ev->ev_fd, sigh) == SIG_ERR) { +		return -1; +	} +	return 0; +} + +/** remove signal handler */ +int signal_del(struct event* ev) +{ +	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG) +		return -1; +	ev->ev_base->signals[ev->ev_fd] = NULL; +	ev->added = 0; +	return 0; +} + +#else /* USE_MINI_EVENT */ +#ifndef USE_WINSOCK +int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) +{ +	return 0; +} +#endif /* not USE_WINSOCK */ +#endif /* USE_MINI_EVENT */ | 
