From ac612570642ede6790e44483286624f400ab2fba Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 1 Mar 2014 07:01:33 +0100 Subject: [PATCH] lib: Added a kqueue mtio implementation. --- configure.in | 22 ++++- lib/Makefile.am | 4 + lib/mtio-kqueue.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 lib/mtio-kqueue.c diff --git a/configure.in b/configure.in index c8b97d6..80d2389 100644 --- a/configure.in +++ b/configure.in @@ -59,7 +59,27 @@ fi if test "$HAS_EPOLL" = yes; then AC_DEFINE(HAVE_EPOLL) fi -AM_CONDITIONAL(USE_EPOLL, [test "$HAS_EPOLL" = yes ]) + +AH_TEMPLATE(HAVE_KQUEUE, [define to enable kqueue support]) +AC_ARG_WITH(kqueue, AS_HELP_STRING([--with-kqueue], [enable kqueue(2) support])) +HAS_KQUEUE="" +if test "$with_kqueue" = no; then HAS_QUEUE=no; fi +if test -z "$HAS_KQUEUE"; then + AC_CHECK_FUNC(kqueue, [], [HAS_KQUEUE=no]) +fi +if test -z "$HAS_KQUEUE"; then + AC_CHECK_HEADER(sys/event.h, [], [HAS_KQUEUE=no]) +fi +if test "$HAS_KQUEUE" != no; then HAS_KQUEUE=yes; fi +if test "$with_kqueue" = yes -a "$HAS_KQUEUE" = no; then + AC_MSG_ERROR([*** cannot find kqueue support on this system]) +fi +if test "$HAS_KQUEUE" = yes; then + AC_DEFINE(HAVE_KQUEUE) +fi + +AM_CONDITIONAL(USE_EPOLL, [test "$HAS_EPOLL" = yes]) +AM_CONDITIONAL(USE_KQUEUE, [test "$HAS_KQUEUE" = yes]) AH_TEMPLATE(HAVE_XATTR, [define to compile support for filesystem extended attributes]) AC_ARG_WITH(xattr, AS_HELP_STRING([--with-xattr], [enable XATTR support])) diff --git a/lib/Makefile.am b/lib/Makefile.am index 8116805..30de42a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -5,7 +5,11 @@ libht_a_CFLAGS = -fPIC if USE_EPOLL libht_a_SOURCES += mtio-epoll.c else +if USE_KQUEUE +libht_a_SOURCES += mtio-kqueue.c +else libht_a_SOURCES += mtio-select.c endif +endif pkginclude_HEADERS = utils.h mt.h log.h req.h proc.h mtio.h resp.h cf.h diff --git a/lib/mtio-kqueue.c b/lib/mtio-kqueue.c new file mode 100644 index 0000000..1235973 --- /dev/null +++ b/lib/mtio-kqueue.c @@ -0,0 +1,238 @@ +/* + ashd - A Sane HTTP Daemon + Copyright (C) 2008 Fredrik Tolf + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + +static struct blocker *blockers; + +struct blocker { + struct blocker *n, *p, *n2, *p2; + int fd, reg; + int ev; + time_t to; + struct muth *th; +}; + +static int qfd = -1, fdln = 0; +static int exitstatus; +static struct blocker **fdlist; + +static int regfd(struct blocker *bl) +{ + struct blocker *o; + int prev; + struct kevent evd; + + if(bl->fd >= fdln) { + if(fdlist) { + fdlist = srealloc(fdlist, sizeof(*fdlist) * (bl->fd + 1)); + memset(fdlist + fdln, 0, sizeof(*fdlist) * (bl->fd + 1 - fdln)); + fdln = bl->fd + 1; + } else { + fdlist = szmalloc(sizeof(*fdlist) * (fdln = (bl->fd + 1))); + } + } + for(prev = 0, o = fdlist[bl->fd]; o; o = o->n2) + prev |= o->ev; + if((bl->ev & EV_READ) && !(prev & EV_READ)) { + evd = (struct kevent) { + .flags = EV_ADD, + .ident = bl->fd, + .filter = EVFILT_READ, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_ADD, EVFILT_READ) on fd %i: %s", bl->fd, strerror(errno)); + return(-1); + } + } + if((bl->ev & EV_WRITE) && !(prev & EV_WRITE)) { + evd = (struct kevent) { + .flags = EV_ADD, + .ident = bl->fd, + .filter = EVFILT_WRITE, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_ADD, EVFILT_WRITE) on fd %i: %s", bl->fd, strerror(errno)); + return(-1); + } + } + bl->n2 = fdlist[bl->fd]; + bl->p2 = NULL; + if(fdlist[bl->fd] != NULL) + fdlist[bl->fd]->p2 = bl; + fdlist[bl->fd] = bl; + bl->reg = 1; + return(0); +} + +static void remfd(struct blocker *bl) +{ + struct blocker *o; + struct kevent evd; + int left; + + if(!bl->reg) + return; + if(bl->n2) + bl->n2->p2 = bl->p2; + if(bl->p2) + bl->p2->n2 = bl->n2; + if(bl == fdlist[bl->fd]) + fdlist[bl->fd] = bl->n2; + for(left = 0, o = fdlist[bl->fd]; o; o = o->n2) + left |= o->ev; + if((bl->ev & EV_READ) && !(left & EV_READ)) { + evd = (struct kevent) { + .flags = EV_DELETE, + .ident = bl->fd, + .filter = EVFILT_READ, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_DELETE, EVFILT_READ) on fd %i: %s", bl->fd, strerror(errno)); + } + } + if((bl->ev & EV_WRITE) && !(left & EV_WRITE)) { + evd = (struct kevent) { + .flags = EV_DELETE, + .ident = bl->fd, + .filter = EVFILT_WRITE, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_DELETE, EVFILT_WRITE) on fd %i: %s", bl->fd, strerror(errno)); + } + } + bl->reg = 0; +} + +int block(int fd, int ev, time_t to) +{ + struct blocker *bl; + int rv; + + omalloc(bl); + bl->fd = fd; + bl->ev = ev; + if(to > 0) + bl->to = time(NULL) + to; + bl->th = current; + if((qfd >= 0) && regfd(bl)) { + free(bl); + return(-1); + } + bl->n = blockers; + if(blockers) + blockers->p = bl; + blockers = bl; + rv = yield(); + if(bl->n) + bl->n->p = bl->p; + if(bl->p) + bl->p->n = bl->n; + if(bl == blockers) + blockers = bl->n; + remfd(bl); + free(bl); + return(rv); +} + +int ioloop(void) +{ + struct blocker *bl, *nbl; + struct kevent evs[16]; + int i, fd, nev, ev; + time_t now, timeout; + struct timespec *toval; + + exitstatus = 0; + qfd = kqueue(); + fcntl(qfd, F_SETFD, FD_CLOEXEC); + for(bl = blockers; bl; bl = nbl) { + nbl = bl->n; + if(regfd(bl)) + resume(bl->th, -1); + } + while(blockers != NULL) { + timeout = 0; + for(bl = blockers; bl; bl = bl->n) { + if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to))) + timeout = bl->to; + } + now = time(NULL); + if(timeout == 0) + toval = NULL; + else if(timeout > now) + toval = &(struct timespec){.tv_sec = timeout - now}; + else + toval = &(struct timespec){.tv_sec = 1}; + if(exitstatus) + break; + nev = kevent(qfd, NULL, 0, evs, sizeof(evs) / sizeof(*evs), toval); + if(nev < 0) { + if(errno != EINTR) { + flog(LOG_CRIT, "ioloop: kevent errored out: %s", strerror(errno)); + /* To avoid CPU hogging in case it's bad, which it + * probably is. */ + sleep(1); + } + continue; + } + for(i = 0; i < nev; i++) { + fd = (int)evs[i].ident; + ev = (evs[i].filter == EVFILT_READ)?EV_READ:EV_WRITE; + for(bl = fdlist[fd]; bl; bl = nbl) { + nbl = bl->n2; + if(ev & bl->ev) + resume(bl->th, ev); + } + } + now = time(NULL); + for(bl = blockers; bl; bl = nbl) { + nbl = bl->n; + if((bl->to != 0) && (bl->to <= now)) + resume(bl->th, 0); + } + } + for(bl = blockers; bl; bl = bl->n) + remfd(bl); + close(qfd); + qfd = -1; + return(exitstatus); +} + +void exitioloop(int status) +{ + exitstatus = status; +} -- 2.11.0