diff options
| -rw-r--r-- | src/eloop.c | 183 |
1 files changed, 111 insertions, 72 deletions
diff --git a/src/eloop.c b/src/eloop.c index 6785b316..8b95de3d 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -31,7 +31,9 @@ * of say a few hundred, ppoll(2) performs just fine, if not faster than others. * It also has the smallest memory and binary size footprint. * ppoll(2) is available on all modern OS my software runs on. - * If ppoll is not available, then pselect(2) can be used instead. + * If ppoll is not available, then pselect(2) can be used instead which has + * even smaller memory and binary size footprint. + * However, this difference is quite tiny and the ppoll API is superior. * * Both epoll(7) and kqueue(2) require an extra fd per process to manage * their respective list of interest AND syscalls to manage it. @@ -42,6 +44,12 @@ * kqueue avoids the same limit on OpenBSD. * ppoll can still be secured in both by using SEECOMP or pledge. * + * kqueue can avoid the signal trick we use here so that we function calls + * other than those listed in sigaction(2) in our signal handlers which is + * probably more robust than ours at surviving a signal storm. + * signalfd(2) is available for Linux which probably works in a similar way + * but it's yet another fd to use. + * * Taking this all into account, ppoll(2) is the default mechanism used here. */ @@ -67,12 +75,24 @@ #include "config.h" #endif -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) || defined(HAVE_PPOLL) +/* Prioritise which mechanism we want to use.*/ +#if defined(HAVE_PPOLL) +#undef HAVE_EPOLL +#undef HAVE_KQUEUE +#undef HAVE_PSELECT #elif defined(HAVE_POLLTS) +#define HAVE_PPOLL #define ppoll pollts -#elif defined(HAVE_PSELECT) -#define ppoll eloop_ppoll -#else +#undef HAVE_EPOLL +#undef HAVE_KQUEUE +#undef HAVE_PSELECT +#elif defined(HAVE_KQUEUE) +#undef HAVE_EPOLL +#undef HAVE_PSELECT +#elif defined(HAVE_EPOLL) +#undef HAVE_KQUEUE +#undef HAVE_PSELECT +#elif !defined(HAVE_PSELECT) #define HAVE_PPOLL #endif @@ -88,10 +108,11 @@ #elif defined(HAVE_EPOLL) #include <sys/epoll.h> #define NFD 1 -#else +#elif defined(HAVE_PPOLL) #include <poll.h> -#define USE_POLL #define NFD 1 +#elif defined(HAVE_PSELECT) +#include <sys/select.h> #endif #include "eloop.h" @@ -107,10 +128,6 @@ #endif #endif -#ifdef HAVE_PSELECT -#include <sys/select.h> -#endif - /* Our structures require TAILQ macros, which really every libc should * ship as they are useful beyond belief. * Sadly some libc's don't have sys/queue.h and some that do don't have @@ -162,7 +179,7 @@ struct eloop_event { void *read_cb_arg; void (*write_cb)(void *); void *write_cb_arg; -#ifdef USE_POLL +#ifdef HAVE_PPOLL struct pollfd *pollfd; #endif }; @@ -180,7 +197,6 @@ struct eloop { TAILQ_HEAD (event_head, eloop_event) events; size_t nevents; struct event_head free_events; - bool events_need_setup; struct timespec now; TAILQ_HEAD (timeout_head, eloop_timeout) timeouts; @@ -198,13 +214,16 @@ struct eloop { struct kevent *fds; #elif defined(HAVE_EPOLL) struct epoll_event *fds; -#else +#elif defined(HAVE_PPOLL) struct pollfd *fds; #endif +#if !defined(HAVE_PSELECT) size_t nfds; +#endif - int exitnow; int exitcode; + bool exitnow; + bool events_need_setup; bool cleared; }; @@ -226,46 +245,6 @@ eloop_realloca(void *ptr, size_t n, size_t size) } #endif -#ifdef HAVE_PSELECT -/* Wrapper around pselect, to imitate the ppoll call. */ -int -eloop_ppoll(struct pollfd * fds, nfds_t nfds, - const struct timespec *ts, const sigset_t *sigmask) -{ - fd_set read_fds, write_fds; - nfds_t n; - int maxfd, r; - - FD_ZERO(&read_fds); - FD_ZERO(&write_fds); - maxfd = 0; - for (n = 0; n < nfds; n++) { - if (fds[n].events & POLLIN) { - FD_SET(fds[n].fd, &read_fds); - if (fds[n].fd > maxfd) - maxfd = fds[n].fd; - } - if (fds[n].events & POLLOUT) { - FD_SET(fds[n].fd, &write_fds); - if (fds[n].fd > maxfd) - maxfd = fds[n].fd; - } - } - - r = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask); - if (r > 0) { - for (n = 0; n < nfds; n++) { - fds[n].revents = - FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0; - if (FD_ISSET(fds[n].fd, &write_fds)) - fds[n].revents |= POLLOUT; - } - } - - return r; -} -#endif - unsigned long long eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp, unsigned int *nsp) @@ -344,11 +323,12 @@ eloop_event_setup_fds(struct eloop *eloop) #elif defined(HAVE_EPOLL) struct epoll_event *pfd; size_t nfds = 0; -#else +#elif defined(HAVE_PPOLL) struct pollfd *pfd; size_t nfds = 0; #endif +#ifndef HAVE_PSELECT nfds += eloop->nevents * NFD; if (eloop->nfds < nfds) { pfd = eloop_realloca(eloop->fds, nfds, sizeof(*pfd)); @@ -357,8 +337,9 @@ eloop_event_setup_fds(struct eloop *eloop) eloop->fds = pfd; eloop->nfds = nfds; } +#endif -#ifdef USE_POLL +#ifdef HAVE_PPOLL pfd = eloop->fds; #endif TAILQ_FOREACH_SAFE(e, &eloop->events, next, ne) { @@ -371,7 +352,7 @@ eloop_event_setup_fds(struct eloop *eloop) fprintf(stderr, "%s(%d) fd=%d, rcb=%p, wcb=%p\n", __func__, getpid(), e->fd, e->read_cb, e->write_cb); #endif -#ifdef USE_POLL +#ifdef HAVE_PPOLL e->pollfd = pfd; pfd->fd = e->fd; pfd->events = 0; @@ -484,9 +465,11 @@ setup: } return -1; } -#else +#elif defined(HAVE_PPOLL) e->pollfd = NULL; UNUSED(added); +#else + UNUSED(added); #endif eloop->events_need_setup = true; return 0; @@ -553,7 +536,7 @@ eloop_event_delete_write(struct eloop *eloop, int fd, int write_only) e->fd, &epe) == -1) return -1; } -#else +#elif defined(HAVE_PPOLL) if (e->pollfd != NULL) { e->pollfd->events &= ~POLLOUT; e->pollfd->revents &= ~POLLOUT; @@ -701,7 +684,7 @@ eloop_exit(struct eloop *eloop, int code) assert(eloop != NULL); eloop->exitcode = code; - eloop->exitnow = 1; + eloop->exitnow = true; } void @@ -710,7 +693,7 @@ eloop_enter(struct eloop *eloop) assert(eloop != NULL); - eloop->exitnow = 0; + eloop->exitnow = false; } /* Must be called after fork(2) */ @@ -789,6 +772,7 @@ eloop_forked(struct eloop *eloop) int eloop_open(struct eloop *eloop) { +#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) int fd; assert(eloop != NULL); @@ -807,15 +791,14 @@ eloop_open(struct eloop *eloop) } #elif defined(HAVE_EPOLL) fd = epoll_create1(EPOLL_CLOEXEC); -#else - fd = 0; #endif -#ifndef USE_POLL eloop->fd = fd; -#endif - return fd; +#else + UNUSED(eloop); + return 0; +#endif } int @@ -940,10 +923,12 @@ eloop_new(void) TAILQ_INIT(&eloop->free_timeouts); eloop->exitcode = EXIT_FAILURE; +#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) if (eloop_open(eloop) == -1) { eloop_free(eloop); return NULL; } +#endif return eloop; } @@ -977,6 +962,7 @@ eloop_clear(struct eloop *eloop, ...) } va_end(va1); +#if !defined(HAVE_PSELECT) /* Free the pollfd buffer and ensure it's re-created before * the next run. This allows us to shrink it incase we use a lot less * signals and fds to respond to after forking. */ @@ -984,6 +970,7 @@ eloop_clear(struct eloop *eloop, ...) eloop->fds = NULL; eloop->nfds = 0; eloop->events_need_setup = true; +#endif while ((e = TAILQ_FIRST(&eloop->free_events))) { TAILQ_REMOVE(&eloop->free_events, e, next); @@ -1014,7 +1001,7 @@ eloop_free(struct eloop *eloop) #if defined(HAVE_KQUEUE) static int -eloop_run_kqueue(struct eloop *eloop, struct timespec *ts) +eloop_run_kqueue(struct eloop *eloop, const struct timespec *ts) { int n, nn; struct kevent *ke; @@ -1054,7 +1041,8 @@ eloop_run_kqueue(struct eloop *eloop, struct timespec *ts) #elif defined(HAVE_EPOLL) static int -eloop_run_epoll(struct eloop *eloop, struct timespec *ts, sigset_t *signals) +eloop_run_epoll(struct eloop *eloop, + const struct timespec *ts, const sigset_t *signals) { int timeout, n, nn; struct epoll_event *epe; @@ -1094,10 +1082,11 @@ eloop_run_epoll(struct eloop *eloop, struct timespec *ts, sigset_t *signals) return n; } -#else +#elif defined(HAVE_PPOLL) static int -eloop_run_ppoll(struct eloop *eloop, struct timespec *ts, sigset_t *signals) +eloop_run_ppoll(struct eloop *eloop, + const struct timespec *ts, const sigset_t *signals) { int n, nn; struct eloop_event *e; @@ -1127,6 +1116,52 @@ eloop_run_ppoll(struct eloop *eloop, struct timespec *ts, sigset_t *signals) } return n; } + +#elif defined(HAVE_PSELECT) + +static int +eloop_run_pselect(struct eloop *eloop, + const struct timespec *ts, const sigset_t *sigmask) +{ + fd_set read_fds, write_fds; + int maxfd, n; + struct eloop_event *e; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + maxfd = 0; + TAILQ_FOREACH(e, &eloop->events, next) { + if (e->fd == -1) + continue; + if (e->read_cb != NULL) { + FD_SET(e->fd, &read_fds); + if (e->fd > maxfd) + maxfd = e->fd; + } + if (e->write_cb != NULL) { + FD_SET(e->fd, &write_fds); + if (e->fd > maxfd) + maxfd = e->fd; + } + } + + n = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask); + if (n == -1 || n == 0) + return n; + + TAILQ_FOREACH(e, &eloop->events, next) { + if (eloop->cleared) + break; + if (e->fd == -1) + continue; + if (e->write_cb != NULL && FD_ISSET(e->fd, &write_fds)) + e->write_cb(e->write_cb_arg); + if (e->read_cb != NULL && FD_ISSET(e->fd, &read_fds)) + e->read_cb(e->read_cb_arg); + } + + return n; +} #endif int @@ -1190,8 +1225,12 @@ eloop_start(struct eloop *eloop, sigset_t *signals) error = eloop_run_kqueue(eloop, tsp); #elif defined(HAVE_EPOLL) error = eloop_run_epoll(eloop, tsp, signals); -#else +#elif defined(HAVE_PPOLL) error = eloop_run_ppoll(eloop, tsp, signals); +#elif defined(HAVE_PSELECT) + error = eloop_run_pselect(eloop, tsp, signals); +#else +#error no polling mechanism to run! #endif if (error == -1) { if (errno == EINTR) |
