From 7a0c530b5645ea0b50e7524eae9ddab170293a18 Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Wed, 11 May 2005 04:08:51 +0000 Subject: [PATCH] performance improvements of select handler by Nick Mathewson; I added better recovery when memory allocation fails; something that needs to be done for the poll improvements, too. svn:r166 --- select.c | 247 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 178 insertions(+), 69 deletions(-) diff --git a/select.c b/select.c index b70a780c..00909b79 100644 --- a/select.c +++ b/select.c @@ -44,6 +44,9 @@ #include #include #include +#ifdef CHECK_INVARIANTS +#include +#endif #include "event.h" #include "event-internal.h" @@ -59,8 +62,12 @@ extern volatile sig_atomic_t evsignal_caught; struct selectop { int event_fds; /* Highest fd in fd set */ int event_fdsz; - fd_set *event_readset; - fd_set *event_writeset; + fd_set *event_readset_in; + fd_set *event_writeset_in; + fd_set *event_readset_out; + fd_set *event_writeset_out; + struct event **event_r_by_fd; + struct event **event_w_by_fd; sigset_t evsigmask; }; @@ -79,6 +86,8 @@ const struct eventop selectops = { select_dispatch }; +static int select_resize(struct selectop *sop, int fdsz); + void * select_init(void) { @@ -91,11 +100,40 @@ select_init(void) if (!(sop = calloc(1, sizeof(struct selectop)))) return (NULL); + select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask)); + evsignal_init(&sop->evsigmask); return (sop); } +#ifdef CHECK_INVARIANTS +static void +check_selectop(struct selectop *sop) +{ + int i; + for (i=0;i<=sop->event_fds;++i) { + if (FD_ISSET(i, sop->event_readset_in)) { + assert(sop->event_r_by_fd[i]); + assert(sop->event_r_by_fd[i]->ev_events & EV_READ); + assert(sop->event_r_by_fd[i]->ev_fd == i); + } else { + assert(! sop->event_r_by_fd[i]); + } + if (FD_ISSET(i, sop->event_writeset_in)) { + assert(sop->event_w_by_fd[i]); + assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE); + assert(sop->event_w_by_fd[i]->ev_fd == i); + } else { + assert(! sop->event_w_by_fd[i]); + } + } + +} +#else +#define check_selectop(sop) +#endif + /* * Called with the highest fd that we know about. If it is 0, completely * recalculate everything. @@ -105,41 +143,8 @@ int select_recalc(struct event_base *base, void *arg, int max) { struct selectop *sop = arg; - fd_set *readset, *writeset; - struct event *ev; - int fdsz; - - if (sop->event_fds < max) - sop->event_fds = max; - if (!sop->event_fds) { - TAILQ_FOREACH(ev, &base->eventqueue, ev_next) - if (ev->ev_fd > sop->event_fds) - sop->event_fds = ev->ev_fd; - } - - fdsz = howmany(sop->event_fds + 1, NFDBITS) * sizeof(fd_mask); - if (fdsz > sop->event_fdsz) { - if ((readset = realloc(sop->event_readset, fdsz)) == NULL) { - event_warn("malloc"); - return (-1); - } - - if ((writeset = realloc(sop->event_writeset, fdsz)) == NULL) { - event_warn("malloc"); - free(readset); - return (-1); - } - - memset((char *)readset + sop->event_fdsz, 0, - fdsz - sop->event_fdsz); - memset((char *)writeset + sop->event_fdsz, 0, - fdsz - sop->event_fdsz); - - sop->event_readset = readset; - sop->event_writeset = writeset; - sop->event_fdsz = fdsz; - } + check_selectop(sop); return (evsignal_recalc(&sop->evsigmask)); } @@ -147,26 +152,23 @@ select_recalc(struct event_base *base, void *arg, int max) int select_dispatch(struct event_base *base, void *arg, struct timeval *tv) { - int maxfd, res; - struct event *ev, *next; + int res, i; struct selectop *sop = arg; - memset(sop->event_readset, 0, sop->event_fdsz); - memset(sop->event_writeset, 0, sop->event_fdsz); + check_selectop(sop); - TAILQ_FOREACH(ev, &base->eventqueue, ev_next) { - if (ev->ev_events & EV_WRITE) - FD_SET(ev->ev_fd, sop->event_writeset); - if (ev->ev_events & EV_READ) - FD_SET(ev->ev_fd, sop->event_readset); - } + memcpy(sop->event_readset_out, sop->event_readset_in, + sop->event_fdsz); + memcpy(sop->event_writeset_out, sop->event_writeset_in, + sop->event_fdsz); if (evsignal_deliver(&sop->evsigmask) == -1) return (-1); - res = select(sop->event_fds + 1, sop->event_readset, - sop->event_writeset, NULL, tv); + res = select(sop->event_fds + 1, sop->event_readset_out, + sop->event_writeset_out, NULL, tv); + check_selectop(sop); if (evsignal_recalc(&sop->evsigmask) == -1) return (-1); @@ -183,32 +185,94 @@ select_dispatch(struct event_base *base, void *arg, struct timeval *tv) event_debug(("%s: select reports %d", __func__, res)); - maxfd = 0; - for (ev = TAILQ_FIRST(&base->eventqueue); ev != NULL; ev = next) { - next = TAILQ_NEXT(ev, ev_next); - + check_selectop(sop); + for (i = 0; i <= sop->event_fds; ++i) { + struct event *r_ev = NULL, *w_ev = NULL; res = 0; - if (FD_ISSET(ev->ev_fd, sop->event_readset)) + if (FD_ISSET(i, sop->event_readset_out)) { + r_ev = sop->event_r_by_fd[i]; res |= EV_READ; - if (FD_ISSET(ev->ev_fd, sop->event_writeset)) + } + if (FD_ISSET(i, sop->event_writeset_out)) { + w_ev = sop->event_w_by_fd[i]; res |= EV_WRITE; - res &= ev->ev_events; - - if (res) { - if (!(ev->ev_events & EV_PERSIST)) - event_del(ev); - else if (ev->ev_fd > maxfd) - maxfd = ev->ev_fd; - event_active(ev, res, 1); - } else if (ev->ev_fd > maxfd) - maxfd = ev->ev_fd; + } + if (r_ev && (res & r_ev->ev_events)) { + if (!(r_ev->ev_events & EV_PERSIST)) + event_del(r_ev); + event_active(r_ev, res & r_ev->ev_events, 1); + } + if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { + if (!(w_ev->ev_events & EV_PERSIST)) + event_del(w_ev); + event_active(w_ev, res & w_ev->ev_events, 1); + } } + check_selectop(sop); - sop->event_fds = maxfd; + return (0); +} + + +static int +select_resize(struct selectop *sop, int fdsz) +{ + int n_events, n_events_old; + + fd_set *readset_in = NULL; + fd_set *writeset_in = NULL; + fd_set *readset_out = NULL; + fd_set *writeset_out = NULL; + struct event **r_by_fd = NULL; + struct event **w_by_fd = NULL; + + n_events = (fdsz/sizeof(fd_mask)) * NFDBITS; + n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS; + + if (sop->event_readset_in) + check_selectop(sop); + + if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL) + goto error; + sop->event_readset_in = readset_in; + if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL) + goto error; + sop->event_readset_out = readset_out; + if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL) + goto error; + sop->event_writeset_in = writeset_in; + if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL) + goto error; + sop->event_writeset_out = writeset_out; + if ((r_by_fd = realloc(sop->event_r_by_fd, + n_events*sizeof(struct event*))) == NULL) + goto error; + sop->event_r_by_fd = r_by_fd; + if ((w_by_fd = realloc(sop->event_w_by_fd, + n_events * sizeof(struct event*))) == NULL) + goto error; + sop->event_w_by_fd = w_by_fd; + + memset((char *)sop->event_readset_in + sop->event_fdsz, 0, + fdsz - sop->event_fdsz); + memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, + fdsz - sop->event_fdsz); + memset(sop->event_r_by_fd + n_events_old, 0, + (n_events-n_events_old) * sizeof(struct event*)); + memset(sop->event_w_by_fd + n_events_old, 0, + (n_events-n_events_old) * sizeof(struct event*)); + + sop->event_fdsz = fdsz; + check_selectop(sop); return (0); + + error: + event_warn("malloc"); + return (-1); } + int select_add(void *arg, struct event *ev) { @@ -217,12 +281,40 @@ select_add(void *arg, struct event *ev) if (ev->ev_events & EV_SIGNAL) return (evsignal_add(&sop->evsigmask, ev)); - /* + check_selectop(sop); + /* * Keep track of the highest fd, so that we can calculate the size * of the fd_sets for select(2) */ - if (sop->event_fds < ev->ev_fd) + if (sop->event_fds < ev->ev_fd) { + int fdsz = sop->event_fdsz; + + if (fdsz < sizeof(fd_mask)) + fdsz = sizeof(fd_mask); + + while (fdsz < + (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask))) + fdsz *= 2; + + if (fdsz != sop->event_fdsz) { + if (select_resize(sop, fdsz)) { + check_selectop(sop); + return (-1); + } + } + sop->event_fds = ev->ev_fd; + } + + if (ev->ev_events & EV_READ) { + FD_SET(ev->ev_fd, sop->event_readset_in); + sop->event_r_by_fd[ev->ev_fd] = ev; + } + if (ev->ev_events & EV_WRITE) { + FD_SET(ev->ev_fd, sop->event_writeset_in); + sop->event_w_by_fd[ev->ev_fd] = ev; + } + check_selectop(sop); return (0); } @@ -236,8 +328,25 @@ select_del(void *arg, struct event *ev) { struct selectop *sop = arg; - if (!(ev->ev_events & EV_SIGNAL)) + check_selectop(sop); + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(&sop->evsigmask, ev)); + + if (sop->event_fds < ev->ev_fd) { + check_selectop(sop); return (0); + } + + if (ev->ev_events & EV_READ) { + FD_CLR(ev->ev_fd, sop->event_readset_in); + sop->event_r_by_fd[ev->ev_fd] = NULL; + } - return (evsignal_del(&sop->evsigmask, ev)); + if (ev->ev_events & EV_WRITE) { + FD_CLR(ev->ev_fd, sop->event_writeset_in); + sop->event_w_by_fd[ev->ev_fd] = NULL; + } + + check_selectop(sop); + return (0); } -- 2.40.0