]> granicus.if.org Git - libevent/commitdiff
evport: Remove artificial low limit on max events per getn call
authorNick Mathewson <nickm@torproject.org>
Fri, 27 May 2011 18:41:24 +0000 (14:41 -0400)
committerNick Mathewson <nickm@torproject.org>
Fri, 27 May 2011 19:10:46 +0000 (15:10 -0400)
Previously, evport could only handle up to 8 events per time through
the loop.  This would impose an artificially high number of syscalls
on us to retrieve a large number of events.

This code allows the EVLOOP_* flags to work more similarly to how
they do on other platforms (up to 4096 events per syscall).  Dispite
dire warning (from 2006), doing this won't impose a big incremental
RAM penalty: if you have 4096 events firing at once, you necessarily
have a proportional number of events pending, and their associated
data structures are already costing a good bit of RAM.

This patch makes the main/many_events_slow_add test pass again.

evport.c

index a52b436ef8ffa4f9b856cfa92c71a68607cc0327..f7f5597466be127cae57f8d99aa258c53ce38632 100644 (file)
--- a/evport.c
+++ b/evport.c
 #include "evsignal-internal.h"
 #include "evmap-internal.h"
 
-/*
- * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
- * any particular call. You can speed things up by increasing this, but it will
- * (obviously) require more memory.
- */
-#define EVENTS_PER_GETN 8
+#define INITIAL_EVENTS_PER_GETN 8
+#define MAX_EVENTS_PER_GETN 4096
 
 /*
  * Per-file-descriptor information about what events we're subscribed to. These
@@ -104,8 +100,13 @@ struct evport_data {
        int             ed_port;        /* event port for system events  */
        /* How many elements of ed_pending should we look at? */
        int ed_npending;
+       /* How many elements are allocated in ed_pending and pevtlist? */
+       int ed_maxevents;
        /* fdi's that we need to reassoc */
-       int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */
+       int *ed_pending;
+       /* storage space for incoming events. */ 
+       port_event_t *ed_pevtlist;
+       
 };
 
 static void*   evport_init(struct event_base *);
@@ -113,6 +114,7 @@ static int evport_add(struct event_base *, int fd, short old, short events, void
 static int evport_del(struct event_base *, int fd, short old, short events, void *);
 static int     evport_dispatch(struct event_base *, struct timeval *);
 static void    evport_dealloc(struct event_base *);
+static int     grow(struct evport_data *, int min_events);
 
 const struct eventop evportops = {
        "evport",
@@ -134,7 +136,6 @@ static void*
 evport_init(struct event_base *base)
 {
        struct evport_data *evpd;
-//     int i;
 
        if (!(evpd = mm_calloc(1, sizeof(struct evport_data))))
                return (NULL);
@@ -144,6 +145,12 @@ evport_init(struct event_base *base)
                return (NULL);
        }
 
+       if (grow(evpd, INITIAL_EVENTS_PER_GETN) < 0) {
+               close(evpd->ed_port);
+               mm_free(evpd);
+               return NULL;
+       }
+               
        evpd->ed_npending = 0;
 
        evsig_init(base);
@@ -151,6 +158,34 @@ evport_init(struct event_base *base)
        return (evpd);
 }
 
+static int
+grow(struct evport_data *data, int min_events)
+{
+       int newsize;
+       int *new_pending;
+       port_event_t *new_pevtlist;
+       if (data->ed_maxevents) {
+               newsize = data->ed_maxevents;
+               do {
+                       newsize *= 2;
+               } while (newsize < min_events);
+       } else {
+               newsize = min_events;
+       }
+
+       new_pending = mm_realloc(data->ed_pending, sizeof(int)*newsize);
+       if (new_pending == NULL)
+               return -1;
+       data->ed_pending = new_pending;
+       new_pevtlist = mm_realloc(data->ed_pevtlist, sizeof(port_event_t)*newsize);
+       if (new_pevtlist == NULL)
+               return -1;
+       data->ed_pevtlist = new_pevtlist; 
+
+       data->ed_maxevents = newsize;
+       return 0;
+}
+
 #ifdef CHECK_INVARIANTS
 /*
  * Checks some basic properties about the evport_data structure. Because it
@@ -217,12 +252,12 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
 {
        int i, res;
        struct evport_data *epdp = base->evbase;
-       port_event_t pevtlist[EVENTS_PER_GETN];
+       port_event_t *pevtlist = epdp->ed_pevtlist;
 
        /*
         * port_getn will block until it has at least nevents events. It will
         * also return how many it's given us (which may be more than we asked
-        * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
+        * for, as long as it's less than our maximum (ed_maxevents)) in
         * nevents.
         */
        int nevents = 1;
@@ -263,7 +298,7 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
 
        EVBASE_RELEASE_LOCK(base, th_base_lock);
 
-       res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
+       res = port_getn(epdp->ed_port, pevtlist, epdp->ed_maxevents,
            (unsigned int *) &nevents, ts_p);
 
        EVBASE_ACQUIRE_LOCK(base, th_base_lock);
@@ -317,6 +352,13 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
        } /* end of all events gotten */
        epdp->ed_npending = nevents;
 
+       if (nevents == epdp->ed_maxevents &&
+           epdp->ed_maxevents < MAX_EVENTS_PER_GETN) {
+               /* we used all the space this time.  We should be ready
+                * for more events next time around. */
+               grow(epdp, epdp->ed_maxevents * 2);
+       }
+
        check_evportop(epdp);
 
        return (0);
@@ -393,5 +435,10 @@ evport_dealloc(struct event_base *base)
 
        close(evpd->ed_port);
 
+       if (evpd->ed_pending)
+               mm_free(evpd->ed_pending);
+       if (evpd->ed_pevtlist)
+               mm_free(evpd->ed_pevtlist);
+
        mm_free(evpd);
 }