From: Wouter Wijngaards Date: Fri, 16 Feb 2007 21:26:10 +0000 (+0000) Subject: alternative for libevent. Select() only, only the features needed. X-Git-Tag: release-0.0~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b310215e81ee6d2833b7914129da4ed0b642d1e4;p=thirdparty%2Funbound.git alternative for libevent. Select() only, only the features needed. git-svn-id: file:///svn/unbound/trunk@121 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/configure.ac b/configure.ac index 161d989ba..85c273a70 100644 --- a/configure.ac +++ b/configure.ac @@ -354,7 +354,7 @@ AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname], ],[ withval="yes" ]) - if test x_$withval != x_no; then +if test x_$withval != x_no; then AC_MSG_CHECKING(for libevent) if test x_$withval = x_ -o x_$withval = x_yes; then withval="/usr/local /usr/lib /usr/pkg /usr/sfw /usr"; @@ -382,10 +382,12 @@ AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname], RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib" fi AC_SUBST(RUNTIME_PATH) - fi -AC_SEARCH_LIBS(event_set, [event]) -AC_CHECK_HEADERS([event.h],,, [AC_INCLUDES_DEFAULT]) -AC_CHECK_FUNCS([event_base_free]) # only in libevent 1.2 and later + AC_SEARCH_LIBS(event_set, [event]) + AC_CHECK_HEADERS([event.h],,, [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS([event_base_free]) # only in libevent 1.2 and later +else + AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events]) +fi # check to see if libraries are needed for these functions. AC_CHECK_LIB(socket, socket) diff --git a/doc/Changelog b/doc/Changelog index 955fcc276..2248faaea 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,9 @@ - removed check decls, we can compile without them. - makefile supports LIBOBJ replacements. - docs checks ignore compat code. + - added util/mini-event.c and .h, a select based alternative used with + ./configure --with-libevent=no + It is limited to 1024 file descriptors, and has less features. 15 February 2007: Wouter - port to FreeBSD 4.11 Dec Alpha. Also works on Solaris 10 sparc64, diff --git a/util/mini_event.c b/util/mini_event.c new file mode 100644 index 000000000..b1578a935 --- /dev/null +++ b/util/mini_event.c @@ -0,0 +1,318 @@ +/* + * 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 USE_MINI_EVENT +#include +#include "util/mini_event.h" + +/** compare events in tree, based on timevalue, ptr for uniqueness */ +static int 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; +} + +/** create event base */ +void *event_init(void) +{ + struct event_base* base = (struct event_base*)malloc( + sizeof(struct event_base)); + if(!base) + return NULL; + memset(base, 0, sizeof(*base)); + base->times = rbtree_create(ev_cmp); + if(!base->times) { + event_base_free(base); + return NULL; + } + base->fds = (struct event**)calloc(MAX_FDS, 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, time_t now, time_t *wait) +{ + struct event* p, *np; + *wait = (time_t)-1; + + p = (struct event*)rbtree_first(base->times); + while((rbnode_t*)p!=RBTREE_NULL) { + /* store next to make deletion possible */ + np = (struct event*)rbtree_next((rbnode_t*)p); + if(!p || p->ev_timeout.tv_sec > now || + (p->ev_timeout.tv_sec==now && p->ev_timeout.tv_usec>0)) { + /* there is a next larger timeout. wait for it */ + *wait = p->ev_timeout.tv_sec - now; + if(p->ev_timeout.tv_usec > 0) + *wait+=1; /* wait a bit longer */ + return; + } + /* event times out, remove it */ + (void)rbtree_delete(base->times, p); + p->ev_events &= ~EV_TIMEOUT; + (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg); + /* next is a valid pointer and next larger element */ + p = np; + } +} + +/** call select and callbacks for that */ +static int handle_select(struct event_base* base, time_t wait) +{ + struct timeval tv; + fd_set r, w; + int ret, i; + + tv.tv_sec = wait; + tv.tv_usec = 0; + memmove(&r, &base->reads, sizeof(fd_set)); + memmove(&w, &base->writes, sizeof(fd_set)); + + if((ret = select(base->maxfd+1, &r, &w, NULL, + (wait==(time_t)-1)?NULL:&tv)) == -1) { + return -1; + } + + for(i=0; imaxfd+1; i++) { + short bits = 0; + if(!base->fds[i]) { + 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) { + (*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) +{ + while(!base->need_to_exit) + { + time_t now = time(NULL), wait; + /* see if timeouts need handling */ + handle_timeouts(base, now, &wait); + /* 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->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; + 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 >= MAX_FDS) + 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(ev->ev_fd, &ev->ev_base->reads); + if(ev->ev_events&EV_WRITE) + FD_SET(ev->ev_fd, &ev->ev_base->writes); + 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 + ev->ev_timeout.tv_sec = tv->tv_sec + time(NULL); + ev->ev_timeout.tv_usec = tv->tv_usec; +#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 >= MAX_FDS) + 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(ev->ev_fd, &ev->ev_base->reads); + FD_CLR(ev->ev_fd, &ev->ev_base->writes); + } + 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; + (*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; +} + +#endif /* USE_MINI_EVENT */ diff --git a/util/mini_event.h b/util/mini_event.h new file mode 100644 index 000000000..a02c0d361 --- /dev/null +++ b/util/mini_event.h @@ -0,0 +1,146 @@ +/* + * mini-event.h - micro implementation of libevent api, using select() only. + * + * 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 + * This file implements part of the event(3) libevent api. + * The back end is only select. Max number of fds is limited. + */ + +#ifndef MINI_EVENT_H +#define MINI_EVENT_H + +#ifdef USE_MINI_EVENT + +#ifndef HAVE_EVENT_BASE_FREE +#define HAVE_EVENT_BASE_FREE +#endif + +/** event timeout */ +#define EV_TIMEOUT 0x01 +/** event fd readable */ +#define EV_READ 0x02 +/** event fd writable */ +#define EV_WRITE 0x04 +/** event signal */ +#define EV_SIGNAL 0x08 +/** event must persist */ +#define EV_PERSIST 0x10 + +/* needs our redblack tree */ +#include "rbtree.h" + +/** max number of file descriptors to support */ +#define MAX_FDS 1024 +/** max number of signals to support */ +#define MAX_SIG 32 + +/** event base */ +struct event_base +{ + /** sorted by timeout (absolute), ptr */ + rbtree_t* times; + /** array of 0 - maxfd of ptr to event for it */ + struct event** fds; + /** max fd in use */ + int maxfd; + /** fdset for read write */ + fd_set reads, writes; + /** array of 0 - maxsig of ptr to event for it */ + struct event** signals; + /** if we need to exit */ + int need_to_exit; +}; + +/** + * Event structure. Has some of the event elements. + */ +struct event { + /** node in timeout rbtree */ + rbnode_t node; + /** is event already added */ + int added; + + /** event base it belongs to */ + struct event_base *ev_base; + /** fd to poll or -1 for timeouts */ + int ev_fd; + /** events this event is interested in */ + short ev_events; + /** timeout value */ + struct timeval ev_timeout; + + /** callback to call: fd, eventbits, userarg */ + void (*ev_callback)(int, short, void *arg); + /** callback user arg */ + void *ev_arg; +}; + +/* function prototypes as they appear in event.h */ +/** create event base */ +void *event_init(void); +/** get version */ +const char *event_get_version(void); +/** get polling method, select */ +const char *event_get_method(void); +/** run select in a loop */ +int event_base_dispatch(struct event_base *); +/** exit that loop */ +int event_base_loopexit(struct event_base *, struct timeval *); +/** free event base. Free events yourself */ +void event_base_free(struct event_base *); +/** set content of event */ +void event_set(struct event *, int, short, void (*)(int, short, void *), void *); +/** add event to a base */ +int event_base_set(struct event_base *, struct event *); +/** add event to make it active. You may not change it with event_set anymore */ +int event_add(struct event *, struct timeval *); +/** remove event. You may change it again */ +int event_del(struct event *); + +#define evtimer_add(ev, tv) event_add(ev, tv) +#define evtimer_del(ev) event_del(ev) + +/* uses different implementation. Cannot mix fd/timeouts and signals. */ +/** install signal handler */ +int signal_add(struct event *, struct timeval *); +/** set signal event contents */ +#define signal_set(ev, x, cb, arg) \ + event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg) +/** remove signal handler */ +int signal_del(struct event *); + +#endif /* USE_MINI_EVENT */ +#endif /* MINI_EVENT_H */ diff --git a/util/netevent.c b/util/netevent.c index 1d99cb683..c664560f7 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -49,8 +49,12 @@ /* We define libevent structures here to hide the libevent stuff. */ +#ifdef USE_MINI_EVENT +#include "util/mini_event.h" +#else /* we use libevent */ #include +#endif /** * The internal event structure for keeping libevent info for the event. @@ -159,7 +163,8 @@ comm_base_create() free(b); return NULL; } - verbose(VERB_ALGO, "libevent uses %s method.", event_get_method()); + verbose(VERB_ALGO, "libevent %s uses %s method.", + event_get_version(), event_get_method()); return b; }