diff --git a/configure.ac b/configure.ac index f3b35c98..74709630 100644 --- a/configure.ac +++ b/configure.ac @@ -214,6 +214,31 @@ case "$enable_debug_keep_connections_open" in ;; esac + +DEFAULT_EVENTLOOP=select_eventloop +AC_CHECK_HEADERS([sys/poll.h poll.h sys/resource.h],,, [AC_INCLUDES_DEFAULT]) +AC_CHECK_FUNCS([getrlimit]) +AC_ARG_ENABLE(poll-eventloop, AC_HELP_STRING([--disable-poll-eventloop], [Disable default eventloop based on poll (default=enabled if available)])) +case "$enable_poll_eventloop" in + no) + ;; + yes|*) +AC_MSG_CHECKING(for poll) +AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#ifdef HAVE_SYS_POLL_H +#include +#else +#include +#endif +], [int rc; rc = poll((struct pollfd *)(0), 0, 0);])], [ +AC_MSG_RESULT(yes) +AC_DEFINE_UNQUOTED([USE_POLL_DEFAULT_EVENTLOOP], [1], [Define this to enable a default eventloop based on poll().]) +DEFAULT_EVENTLOOP=poll_eventloop +],[AC_MSG_RESULT(no)]) + ;; +esac +AC_SUBST(DEFAULT_EVENTLOOP) + AC_ARG_ENABLE(tcp-fastopen, AC_HELP_STRING([--disable-tcp-fastopen], Disable TCP Fast Open (default=enabled if available)), enable_tcp_fastopen="$enableval", enable_tcp_fastopen=yes) if test "x$enable_tcp_fastopen" = xno; then @@ -1076,8 +1101,6 @@ if test "$ac_cv_func_arc4random" = "no"; then ]) fi -AC_DEFINE(USE_MINI_EVENT, 1, [Needed for sync stub resolver functions]) - AC_TYPE_SIGNAL case `uname` in diff --git a/src/Makefile.in b/src/Makefile.in index bcd858e2..94f1500b 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -65,6 +65,8 @@ EXTENSION_LIBUV_LDFLAGS=@EXTENSION_LIBUV_LDFLAGS@ C99COMPATFLAGS=@C99COMPATFLAGS@ +DEFAULT_EVENTLOOP_OBJ=@DEFAULT_EVENTLOOP@.lo + GETDNS_OBJ=const-info.lo convert.lo dict.lo dnssec.lo general.lo \ list.lo request-internal.lo pubkey-pinning.lo rr-dict.lo \ rr-iter.lo server.lo stub.lo sync.lo ub_loop.lo util-internal.lo \ @@ -81,7 +83,7 @@ UTIL_OBJ=rbtree.lo val_secalgo.lo JSMN_OBJ=jsmn.lo -EXTENSION_OBJ=default_eventloop.lo libevent.lo libev.lo +EXTENSION_OBJ=$(DEFAULT_EVENTLOOP_OBJ) libevent.lo libev.lo NON_C99_OBJS=context.lo libuv.lo @@ -152,8 +154,8 @@ libgetdns_ext_ev.la: libgetdns.la libev.lo $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ libev.lo libgetdns.la $(LDFLAGS) $(EXTENSION_LIBEV_LDFLAGS) $(EXTENSION_LIBEV_EXT_LIBS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/extension/libev.symbols -libgetdns.la: $(GETDNS_OBJ) version.lo context.lo default_eventloop.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo default_eventloop.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols +libgetdns.la: $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) + $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols test: all cd test && $(MAKE) $@ diff --git a/src/extension/default_eventloop.h b/src/extension/default_eventloop.h index f4a7d2b6..7b611349 100644 --- a/src/extension/default_eventloop.h +++ b/src/extension/default_eventloop.h @@ -1,6 +1,6 @@ /* * \file default_eventloop.h - * @brief Build in default eventloop extension that uses select. + * @brief Build in default eventloop extension that uses either poll or select. * */ /* @@ -32,29 +32,13 @@ #ifndef DEFAULT_EVENTLOOP_H_ #define DEFAULT_EVENTLOOP_H_ #include "config.h" -#include "getdns/getdns.h" -#include "getdns/getdns_extra.h" -#include "util/uthash.h" - -/* Eventloop based on poll */ - -typedef struct _getdns_eventloop_info { - int id; - getdns_eventloop_event *event; - uint64_t timeout_time; - UT_hash_handle hh; -} _getdns_eventloop_info; - -typedef struct _getdns_default_eventloop { - getdns_eventloop loop; - unsigned int max_fds; - unsigned int max_timeouts; - _getdns_eventloop_info *fd_events; - _getdns_eventloop_info *timeout_events; -} _getdns_default_eventloop; - -void -_getdns_default_eventloop_init(_getdns_default_eventloop *loop); - +#ifdef USE_POLL_DEFAULT_EVENTLOOP +#include "extension/poll_eventloop.h" +#define _getdns_default_eventloop _getdns_poll_eventloop +#define _getdns_default_eventloop_init _getdns_poll_eventloop_init +#else +#include "extension/select_eventloop.h" +#define _getdns_default_eventloop _getdns_select_eventloop +#define _getdns_default_eventloop_init _getdns_select_eventloop_init +#endif #endif - diff --git a/src/extension/default_eventloop.c b/src/extension/poll_eventloop.c similarity index 72% rename from src/extension/default_eventloop.c rename to src/extension/poll_eventloop.c index f9c0e1b2..ede0f20a 100644 --- a/src/extension/default_eventloop.c +++ b/src/extension/poll_eventloop.c @@ -27,15 +27,20 @@ #include "config.h" -#ifndef USE_WINSOCK +#ifdef HAVE_SYS_POLL_H +#include +#else #include #endif +#ifdef HAVE_SYS_RESOURCE_H #include -#include "extension/default_eventloop.h" +#endif +#include "extension/poll_eventloop.h" #include "debug.h" #include "types-internal.h" -_getdns_eventloop_info *find_event(_getdns_eventloop_info** events, int id) +static _getdns_eventloop_info * +find_event(_getdns_eventloop_info** events, int id) { _getdns_eventloop_info* ev; @@ -44,9 +49,10 @@ _getdns_eventloop_info *find_event(_getdns_eventloop_info** events, int id) return ev; } -void add_event(_getdns_eventloop_info** events, int id, _getdns_eventloop_info* ev) +static void +add_event(_getdns_eventloop_info** events, int id, _getdns_eventloop_info* ev) { - DEBUG_SCHED("default_eventloop: add_event with id %d\n", id); + DEBUG_SCHED("poll_eventloop: add_event with id %d\n", id); _getdns_eventloop_info* myevent = calloc(1, sizeof(_getdns_eventloop_info)); myevent->event = ev->event; myevent->id = id; @@ -54,9 +60,10 @@ void add_event(_getdns_eventloop_info** events, int id, _getdns_eventloop_info* HASH_ADD_INT(*events, id, myevent); } -void delete_event(_getdns_eventloop_info** events, _getdns_eventloop_info* ev) +static void +delete_event(_getdns_eventloop_info** events, _getdns_eventloop_info* ev) { - DEBUG_SCHED("default_eventloop: delete_event with id %d\n", ev->id); + DEBUG_SCHED("poll_eventloop: delete_event with id %d\n", ev->id); HASH_DEL(*events, ev); free(ev); } @@ -77,30 +84,33 @@ static uint64_t get_now_plus(uint64_t amount) } static getdns_return_t -default_eventloop_schedule(getdns_eventloop *loop, +poll_eventloop_schedule(getdns_eventloop *loop, int fd, uint64_t timeout, getdns_eventloop_event *event) { - _getdns_default_eventloop *default_loop = (_getdns_default_eventloop *)loop; + _getdns_poll_eventloop *poll_loop = (_getdns_poll_eventloop *)loop; size_t i; DEBUG_SCHED( "%s(loop: %p, fd: %d, timeout: %"PRIu64", event: %p, max_fds: %d)\n" - , __FUNC__, (void *)loop, fd, timeout, (void *)event, default_loop->max_fds); + , __FUNC__, (void *)loop, fd, timeout, (void *)event, poll_loop->max_fds); if (!loop || !event) return GETDNS_RETURN_INVALID_PARAMETER; - if (fd >= (int)default_loop->max_fds) { + +#ifdef HAVE_GETRLIMIT + if (fd >= (int)poll_loop->max_fds) { DEBUG_SCHED( "ERROR: fd %d >= max_fds: %d!\n" - , fd, default_loop->max_fds); + , fd, poll_loop->max_fds); return GETDNS_RETURN_GENERIC_ERROR; } +#endif if (fd >= 0 && !(event->read_cb || event->write_cb)) { DEBUG_SCHED("WARNING: fd event without " "read or write cb!\n"); fd = -1; } if (fd >= 0) { - _getdns_eventloop_info* fd_event = find_event(&default_loop->fd_events, fd); + _getdns_eventloop_info* fd_event = find_event(&poll_loop->fd_events, fd); #if defined(SCHED_DEBUG) && SCHED_DEBUG if (fd_event) { if (fd_event->event == event) { @@ -116,13 +126,13 @@ default_eventloop_schedule(getdns_eventloop *loop, #endif /* cleanup the old event if it exists */ if (fd_event) { - delete_event(&default_loop->fd_events, fd_event); + delete_event(&poll_loop->fd_events, fd_event); } _getdns_eventloop_info fd_ev; event->ev = (void *) (intptr_t) (fd + 1); fd_ev.event = event; fd_ev.timeout_time = get_now_plus(timeout); - add_event(&default_loop->fd_events, fd, &fd_ev); + add_event(&poll_loop->fd_events, fd, &fd_ev); DEBUG_SCHED( "scheduled read/write at fd %d\n", fd); return GETDNS_RETURN_GOOD; @@ -139,12 +149,12 @@ default_eventloop_schedule(getdns_eventloop *loop, DEBUG_SCHED("ERROR: timeout event with write_cb! Clearing.\n"); event->write_cb = NULL; } - for (i = 0; i < default_loop->max_timeouts; i++) { - if (find_event(&default_loop->timeout_events, i) == NULL) { + for (i = poll_loop->timeout_id + 1; i != poll_loop->timeout_id; i++) { + if (find_event(&poll_loop->timeout_events, i) == NULL) { _getdns_eventloop_info timeout_ev; timeout_ev.event = event; timeout_ev.timeout_time = get_now_plus(timeout); - add_event(&default_loop->timeout_events, i, &timeout_ev); + add_event(&poll_loop->timeout_events, i, &timeout_ev); event->ev = (void *) (intptr_t) (i + 1); DEBUG_SCHED( "scheduled timeout at slot %d\n", (int)i); @@ -156,9 +166,9 @@ default_eventloop_schedule(getdns_eventloop *loop, } static getdns_return_t -default_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) +poll_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) { - _getdns_default_eventloop *default_loop = (_getdns_default_eventloop *)loop; + _getdns_poll_eventloop *poll_loop = (_getdns_poll_eventloop *)loop; ssize_t i; if (!loop || !event) @@ -167,11 +177,15 @@ default_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) DEBUG_SCHED( "%s(loop: %p, event: %p)\n", __FUNC__, (void *)loop, (void *)event); i = (intptr_t)event->ev - 1; - if (i < 0 || i > default_loop->max_fds) { + if (i < 0 +#ifdef HAVE_GETRLIMIT + || i > poll_loop->max_fds +#endif + ) { return GETDNS_RETURN_GENERIC_ERROR; } if (event->timeout_cb && !event->read_cb && !event->write_cb) { - _getdns_eventloop_info* timeout_event = find_event(&default_loop->timeout_events, i); + _getdns_eventloop_info* timeout_event = find_event(&poll_loop->timeout_events, i); #if defined(SCHED_DEBUG) && SCHED_DEBUG if (timeout_event && timeout_event->event != event) DEBUG_SCHED( "ERROR: Different/wrong event present at " @@ -180,10 +194,10 @@ default_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) #endif if (timeout_event) { - delete_event(&default_loop->timeout_events, timeout_event); + delete_event(&poll_loop->timeout_events, timeout_event); } } else { - _getdns_eventloop_info* fd_event = find_event(&default_loop->fd_events, i); + _getdns_eventloop_info* fd_event = find_event(&poll_loop->fd_events, i); #if defined(SCHED_DEBUG) && SCHED_DEBUG if (fd_event && fd_event->event != event) DEBUG_SCHED( "ERROR: Different/wrong event present at " @@ -191,7 +205,7 @@ default_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) , (void *)fd_event); #endif if (fd_event) { - delete_event(&default_loop->fd_events, fd_event); + delete_event(&poll_loop->fd_events, fd_event); } } event->ev = NULL; @@ -199,15 +213,15 @@ default_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) } static void -default_eventloop_cleanup(getdns_eventloop *loop) +poll_eventloop_cleanup(getdns_eventloop *loop) { - _getdns_default_eventloop *default_loop = (_getdns_default_eventloop *)loop; - HASH_CLEAR(hh, default_loop->fd_events); - HASH_CLEAR(hh, default_loop->timeout_events); + _getdns_poll_eventloop *poll_loop = (_getdns_poll_eventloop *)loop; + HASH_CLEAR(hh, poll_loop->fd_events); + HASH_CLEAR(hh, poll_loop->timeout_events); } static void -default_read_cb(int fd, getdns_eventloop_event *event) +poll_read_cb(int fd, getdns_eventloop_event *event) { #if !defined(SCHED_DEBUG) || !SCHED_DEBUG (void)fd; @@ -217,7 +231,7 @@ default_read_cb(int fd, getdns_eventloop_event *event) } static void -default_write_cb(int fd, getdns_eventloop_event *event) +poll_write_cb(int fd, getdns_eventloop_event *event) { #if !defined(SCHED_DEBUG) || !SCHED_DEBUG (void)fd; @@ -227,7 +241,7 @@ default_write_cb(int fd, getdns_eventloop_event *event) } static void -default_timeout_cb(int fd, getdns_eventloop_event *event) +poll_timeout_cb(int fd, getdns_eventloop_event *event) { #if !defined(SCHED_DEBUG) || !SCHED_DEBUG (void)fd; @@ -237,9 +251,9 @@ default_timeout_cb(int fd, getdns_eventloop_event *event) } static void -default_eventloop_run_once(getdns_eventloop *loop, int blocking) +poll_eventloop_run_once(getdns_eventloop *loop, int blocking) { - _getdns_default_eventloop *default_loop = (_getdns_default_eventloop *)loop; + _getdns_poll_eventloop *poll_loop = (_getdns_poll_eventloop *)loop; _getdns_eventloop_info *s, *tmp; uint64_t now, timeout = TIMEOUT_FOREVER; size_t i=0; @@ -254,7 +268,7 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) now = get_now_plus(0); - HASH_ITER(hh, default_loop->timeout_events, s, tmp) { + HASH_ITER(hh, poll_loop->timeout_events, s, tmp) { if (now > s->timeout_time) add_event(&timeout_timeout_cbs, s->id, s); else if (s->timeout_time < timeout) @@ -265,10 +279,10 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) HASH_ITER(hh, timeout_timeout_cbs, s, tmp) { getdns_eventloop_event* event = s->event; delete_event(&timeout_timeout_cbs, s); - default_timeout_cb(-1, event); + poll_timeout_cb(-1, event); } // first we count the number of fds that will be active - HASH_ITER(hh, default_loop->fd_events, s, tmp) { + HASH_ITER(hh, poll_loop->fd_events, s, tmp) { if (s->event->read_cb || s->event->write_cb) num_pfds++; @@ -281,7 +295,7 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) pfds = calloc(num_pfds, sizeof(struct pollfd)); i = 0; - HASH_ITER(hh, default_loop->fd_events, s, tmp) { + HASH_ITER(hh, poll_loop->fd_events, s, tmp) { if (s->event->read_cb) { pfds[i].fd = s->id; pfds[i].events |= POLLIN; @@ -312,21 +326,21 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) now = get_now_plus(0); for (i = 0; i < num_pfds; i++) { int fd = pfds[i].fd; - _getdns_eventloop_info* fd_event = find_event(&default_loop->fd_events, fd); + _getdns_eventloop_info* fd_event = find_event(&poll_loop->fd_events, fd); if (fd_event && fd_event->event) { getdns_eventloop_event* event = fd_event->event; if (event->read_cb && (pfds[i].revents & POLLIN)) - default_read_cb(fd, event); + poll_read_cb(fd, event); if (event->write_cb && (pfds[i].revents & POLLOUT)) - default_write_cb(fd, event); + poll_write_cb(fd, event); } } if (pfds) free(pfds); - HASH_ITER(hh, default_loop->fd_events, s, tmp) { + HASH_ITER(hh, poll_loop->fd_events, s, tmp) { if (s->event && s->event->timeout_cb && now > s->timeout_time) @@ -338,9 +352,9 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) int fd = s->id; getdns_eventloop_event* event = s->event; delete_event(&fd_timeout_cbs, s); - default_timeout_cb(fd, event); + poll_timeout_cb(fd, event); } - HASH_ITER(hh, default_loop->timeout_events, s, tmp) { + HASH_ITER(hh, poll_loop->timeout_events, s, tmp) { if (s->event && s->event->timeout_cb && now > s->timeout_time) @@ -351,46 +365,51 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) HASH_ITER(hh, timeout_timeout_cbs, s, tmp) { getdns_eventloop_event* event = s->event; delete_event(&timeout_timeout_cbs, s); - default_timeout_cb(-1, event); + poll_timeout_cb(-1, event); } } static void -default_eventloop_run(getdns_eventloop *loop) +poll_eventloop_run(getdns_eventloop *loop) { - _getdns_default_eventloop *default_loop = (_getdns_default_eventloop *)loop; + _getdns_poll_eventloop *poll_loop = (_getdns_poll_eventloop *)loop; if (!loop) return; /* keep going until all the events are cleared */ - while (default_loop->fd_events || default_loop->timeout_events) { - default_eventloop_run_once(loop, 1); + while (poll_loop->fd_events || poll_loop->timeout_events) { + poll_eventloop_run_once(loop, 1); } } void -_getdns_default_eventloop_init(_getdns_default_eventloop *loop) +_getdns_poll_eventloop_init(_getdns_poll_eventloop *loop) { - static getdns_eventloop_vmt default_eventloop_vmt = { - default_eventloop_cleanup, - default_eventloop_schedule, - default_eventloop_clear, - default_eventloop_run, - default_eventloop_run_once +#ifdef HAVE_GETRLIMIT + struct rlimit rl; +#endif + static getdns_eventloop_vmt poll_eventloop_vmt = { + poll_eventloop_cleanup, + poll_eventloop_schedule, + poll_eventloop_clear, + poll_eventloop_run, + poll_eventloop_run_once }; - (void) memset(loop, 0, sizeof(_getdns_default_eventloop)); - loop->loop.vmt = &default_eventloop_vmt; + (void) memset(loop, 0, sizeof(_getdns_poll_eventloop)); + loop->loop.vmt = &poll_eventloop_vmt; - struct rlimit rl; +#ifdef HAVE_GETRLIMIT if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { loop->max_fds = rl.rlim_cur; - loop->max_timeouts = loop->max_fds; /* this is somewhat arbitrary */ } else { DEBUG_SCHED("ERROR: could not obtain RLIMIT_NOFILE from getrlimit()\n"); +#endif loop->max_fds = 0; - loop->max_timeouts = loop->max_fds; +#if HAVE_GETRLIMIT } +#endif + loop->timeout_id = 0; } diff --git a/src/extension/poll_eventloop.h b/src/extension/poll_eventloop.h new file mode 100644 index 00000000..0c06aa4c --- /dev/null +++ b/src/extension/poll_eventloop.h @@ -0,0 +1,60 @@ +/* + * \file poll_eventloop.h + * @brief Build in default eventloop extension that uses select. + * + */ +/* + * Copyright (c) 2013, NLNet Labs, Verisign, Inc. + * All rights reserved. + * + * 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 names of the copyright holders 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 Verisign, Inc. 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. + */ +#ifndef POLL_EVENTLOOP_H_ +#define POLL_EVENTLOOP_H_ +#include "config.h" +#include "getdns/getdns.h" +#include "getdns/getdns_extra.h" +#include "util/uthash.h" + +/* Eventloop based on poll */ + +typedef struct _getdns_eventloop_info { + int id; + getdns_eventloop_event *event; + uint64_t timeout_time; + UT_hash_handle hh; +} _getdns_eventloop_info; + +typedef struct _getdns_poll_eventloop { + getdns_eventloop loop; + unsigned int max_fds; + unsigned int timeout_id; + _getdns_eventloop_info *fd_events; + _getdns_eventloop_info *timeout_events; +} _getdns_poll_eventloop; + +void +_getdns_poll_eventloop_init(_getdns_poll_eventloop *loop); + +#endif + diff --git a/src/extension/select_eventloop.c b/src/extension/select_eventloop.c new file mode 100644 index 00000000..35aa64b7 --- /dev/null +++ b/src/extension/select_eventloop.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2013, NLNet Labs, Verisign, Inc. + * All rights reserved. + * + * 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 names of the copyright holders 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 Verisign, Inc. 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. + */ + +#include "config.h" + +#include "extension/select_eventloop.h" +#include "debug.h" +#include "types-internal.h" + +static uint64_t get_now_plus(uint64_t amount) +{ + struct timeval tv; + uint64_t now; + + if (gettimeofday(&tv, NULL)) { + perror("gettimeofday() failed"); + exit(EXIT_FAILURE); + } + now = tv.tv_sec * 1000000 + tv.tv_usec; + + return (now + amount * 1000) >= now + ? now + amount * 1000 : TIMEOUT_FOREVER; +} + +static getdns_return_t +select_eventloop_schedule(getdns_eventloop *loop, + int fd, uint64_t timeout, getdns_eventloop_event *event) +{ + _getdns_select_eventloop *select_loop = (_getdns_select_eventloop *)loop; + size_t i; + + DEBUG_SCHED( "%s(loop: %p, fd: %d, timeout: %"PRIu64", event: %p, FD_SETSIZE: %d)\n" + , __FUNC__, (void *)loop, fd, timeout, (void *)event, FD_SETSIZE); + + if (!loop || !event) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (fd >= (int)FD_SETSIZE) { + DEBUG_SCHED( "ERROR: fd %d >= FD_SETSIZE: %d!\n" + , fd, FD_SETSIZE); + return GETDNS_RETURN_GENERIC_ERROR; + } + if (fd >= 0 && !(event->read_cb || event->write_cb)) { + DEBUG_SCHED("WARNING: fd event without " + "read or write cb!\n"); + fd = -1; + } + if (fd >= 0) { +#if defined(SCHED_DEBUG) && SCHED_DEBUG + if (select_loop->fd_events[fd]) { + if (select_loop->fd_events[fd] == event) { + DEBUG_SCHED("WARNING: Event %p not cleared " + "before being rescheduled!\n" + , (void *)select_loop->fd_events[fd]); + } else { + DEBUG_SCHED("ERROR: A different event is " + "already present at fd slot: %p!\n" + , (void *)select_loop->fd_events[fd]); + } + } +#endif + select_loop->fd_events[fd] = event; + select_loop->fd_timeout_times[fd] = get_now_plus(timeout); + event->ev = (void *)(intptr_t)(fd + 1); + DEBUG_SCHED( "scheduled read/write at %d\n", fd); + return GETDNS_RETURN_GOOD; + } + if (!event->timeout_cb) { + DEBUG_SCHED("ERROR: fd < 0 without timeout_cb!\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + if (event->read_cb) { + DEBUG_SCHED("ERROR: timeout event with read_cb! Clearing.\n"); + event->read_cb = NULL; + } + if (event->write_cb) { + DEBUG_SCHED("ERROR: timeout event with write_cb! Clearing.\n"); + event->write_cb = NULL; + } + for (i = 0; i < MAX_TIMEOUTS; i++) { + if (select_loop->timeout_events[i] == NULL) { + select_loop->timeout_events[i] = event; + select_loop->timeout_times[i] = get_now_plus(timeout); + event->ev = (void *)(intptr_t)(i + 1); + DEBUG_SCHED( "scheduled timeout at %d\n", (int)i); + return GETDNS_RETURN_GOOD; + } + } + DEBUG_SCHED("ERROR: Out of timeout slots!\n"); + return GETDNS_RETURN_GENERIC_ERROR; +} + +static getdns_return_t +select_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) +{ + _getdns_select_eventloop *select_loop = (_getdns_select_eventloop *)loop; + ssize_t i; + + if (!loop || !event) + return GETDNS_RETURN_INVALID_PARAMETER; + + DEBUG_SCHED( "%s(loop: %p, event: %p)\n", __FUNC__, (void *)loop, (void *)event); + + i = (intptr_t)event->ev - 1; + if (i < 0 || i >= FD_SETSIZE) { + return GETDNS_RETURN_GENERIC_ERROR; + } + if (event->timeout_cb && !event->read_cb && !event->write_cb) { +#if defined(SCHED_DEBUG) && SCHED_DEBUG + if (select_loop->timeout_events[i] != event) + DEBUG_SCHED( "ERROR: Different/wrong event present at " + "timeout slot: %p!\n" + , (void *)select_loop->timeout_events[i]); +#endif + select_loop->timeout_events[i] = NULL; + } else { +#if defined(SCHED_DEBUG) && SCHED_DEBUG + if (select_loop->fd_events[i] != event) + DEBUG_SCHED( "ERROR: Different/wrong event present at " + "fd slot: %p!\n" + , (void *)select_loop->fd_events[i]); +#endif + select_loop->fd_events[i] = NULL; + } + event->ev = NULL; + return GETDNS_RETURN_GOOD; +} + +static void +select_eventloop_cleanup(getdns_eventloop *loop) +{ + (void)loop; +} + +static void +select_read_cb(int fd, getdns_eventloop_event *event) +{ +#if !defined(SCHED_DEBUG) || !SCHED_DEBUG + (void)fd; +#endif + DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNC__, fd, (void *)event); + event->read_cb(event->userarg); +} + +static void +select_write_cb(int fd, getdns_eventloop_event *event) +{ +#if !defined(SCHED_DEBUG) || !SCHED_DEBUG + (void)fd; +#endif + DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNC__, fd, (void *)event); + event->write_cb(event->userarg); +} + +static void +select_timeout_cb(int fd, getdns_eventloop_event *event) +{ +#if !defined(SCHED_DEBUG) || !SCHED_DEBUG + (void)fd; +#endif + DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNC__, fd, (void *)event); + event->timeout_cb(event->userarg); +} + +static void +select_eventloop_run_once(getdns_eventloop *loop, int blocking) +{ + _getdns_select_eventloop *select_loop = (_getdns_select_eventloop *)loop; + + fd_set readfds, writefds; + int fd, max_fd = -1; + uint64_t now, timeout = TIMEOUT_FOREVER; + size_t i; + struct timeval tv; + + if (!loop) + return; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + now = get_now_plus(0); + + for (i = 0; i < MAX_TIMEOUTS; i++) { + if (!select_loop->timeout_events[i]) + continue; + if (now > select_loop->timeout_times[i]) + select_timeout_cb(-1, select_loop->timeout_events[i]); + else if (select_loop->timeout_times[i] < timeout) + timeout = select_loop->timeout_times[i]; + } + for (fd = 0; fd < (int)FD_SETSIZE; fd++) { + if (!select_loop->fd_events[fd]) + continue; + if (select_loop->fd_events[fd]->read_cb) + FD_SET(fd, &readfds); + if (select_loop->fd_events[fd]->write_cb) + FD_SET(fd, &writefds); + if (fd > max_fd) + max_fd = fd; + if (select_loop->fd_timeout_times[fd] < timeout) + timeout = select_loop->fd_timeout_times[fd]; + } + if (max_fd == -1 && timeout == TIMEOUT_FOREVER) + return; + + if (! blocking || now > timeout) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = (long)((timeout - now) / 1000000); + tv.tv_usec = (long)((timeout - now) % 1000000); + } + if (select(max_fd + 1, &readfds, &writefds, NULL, + (timeout == TIMEOUT_FOREVER ? NULL : &tv)) < 0) { + perror("select() failed"); + exit(EXIT_FAILURE); + } + now = get_now_plus(0); + for (fd = 0; fd < (int)FD_SETSIZE; fd++) { + if (select_loop->fd_events[fd] && + select_loop->fd_events[fd]->read_cb && + FD_ISSET(fd, &readfds)) + select_read_cb(fd, select_loop->fd_events[fd]); + + if (select_loop->fd_events[fd] && + select_loop->fd_events[fd]->write_cb && + FD_ISSET(fd, &writefds)) + select_write_cb(fd, select_loop->fd_events[fd]); + + if (select_loop->fd_events[fd] && + select_loop->fd_events[fd]->timeout_cb && + now > select_loop->fd_timeout_times[fd]) + select_timeout_cb(fd, select_loop->fd_events[fd]); + + i = fd; + if (select_loop->timeout_events[i] && + select_loop->timeout_events[i]->timeout_cb && + now > select_loop->timeout_times[i]) + select_timeout_cb(-1, select_loop->timeout_events[i]); + } +} + +static void +select_eventloop_run(getdns_eventloop *loop) +{ + _getdns_select_eventloop *select_loop = (_getdns_select_eventloop *)loop; + size_t i; + + if (!loop) + return; + + i = 0; + while (i < MAX_TIMEOUTS) { + if (select_loop->fd_events[i] || select_loop->timeout_events[i]) { + select_eventloop_run_once(loop, 1); + i = 0; + } else { + i++; + } + } +} + +void +_getdns_select_eventloop_init(_getdns_select_eventloop *loop) +{ + static getdns_eventloop_vmt select_eventloop_vmt = { + select_eventloop_cleanup, + select_eventloop_schedule, + select_eventloop_clear, + select_eventloop_run, + select_eventloop_run_once + }; + + (void) memset(loop, 0, sizeof(_getdns_select_eventloop)); + loop->loop.vmt = &select_eventloop_vmt; +} diff --git a/src/extension/select_eventloop.h b/src/extension/select_eventloop.h new file mode 100644 index 00000000..40dfb549 --- /dev/null +++ b/src/extension/select_eventloop.h @@ -0,0 +1,58 @@ +/* + * \file select_eventloop.h + * @brief Build in default eventloop extension that uses select. + * + */ +/* + * Copyright (c) 2013, NLNet Labs, Verisign, Inc. + * All rights reserved. + * + * 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 names of the copyright holders 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 Verisign, Inc. 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. + */ +#ifndef SELECT_EVENTLOOP_H_ +#define SELECT_EVENTLOOP_H_ +#include "config.h" +#include "getdns/getdns.h" +#include "getdns/getdns_extra.h" + +/* No more than select's capability queries can be outstanding, + * The number of outstanding timeouts should be less or equal then + * the number of outstanding queries, so MAX_TIMEOUTS equal to + * FD_SETSIZE should be safe. + */ +#define MAX_TIMEOUTS FD_SETSIZE + +/* Eventloop based on select */ +typedef struct _getdns_select_eventloop { + getdns_eventloop loop; + getdns_eventloop_event *fd_events[FD_SETSIZE]; + uint64_t fd_timeout_times[FD_SETSIZE]; + getdns_eventloop_event *timeout_events[MAX_TIMEOUTS]; + uint64_t timeout_times[MAX_TIMEOUTS]; +} _getdns_select_eventloop; + + +void +_getdns_select_eventloop_init(_getdns_select_eventloop *loop); + +#endif