]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
alternative for libevent. Select() only, only the features needed.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 16 Feb 2007 21:26:10 +0000 (21:26 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 16 Feb 2007 21:26:10 +0000 (21:26 +0000)
git-svn-id: file:///svn/unbound/trunk@121 be551aaa-1e26-0410-a405-d3ace91eadb9

configure.ac
doc/Changelog
util/mini_event.c [new file with mode: 0644]
util/mini_event.h [new file with mode: 0644]
util/netevent.c

index 161d989baf208fc0cda4e1ce2e2ee605c513cb49..85c273a70cb109b92b34285d604d5256d7d478c8 100644 (file)
@@ -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)
index 955fcc27687e69af3f783edf86e4e9a84b592412..2248faaea504e4b3b7f6ea430ed314ce612a7fd3 100644 (file)
@@ -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 (file)
index 0000000..b1578a9
--- /dev/null
@@ -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 <signal.h>
+#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; i<base->maxfd+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 (file)
index 0000000..a02c0d3
--- /dev/null
@@ -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 */
index 1d99cb6833228e6487d5cd0830c642907075c649..c664560f7b0a55618206ef3596b2adab932ff873 100644 (file)
 
 /* We define libevent structures here to hide the libevent stuff. */
 
+#ifdef USE_MINI_EVENT
+#include "util/mini_event.h"
+#else
 /* we use libevent */
 #include <event.h>
+#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;
 }