From c04d927637141a38cfcc648fe51b403ae6202273 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 27 May 2011 14:41:24 -0400 Subject: [PATCH] evport: Remove artificial low limit on max events per getn call 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 | 69 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/evport.c b/evport.c index a52b436e..f7f55974 100644 --- a/evport.c +++ b/evport.c @@ -73,12 +73,8 @@ #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); } -- 2.40.0