summaryrefslogtreecommitdiff
path: root/kqueue.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2011-05-03 13:54:57 -0400
committerNick Mathewson <nickm@torproject.org>2011-05-03 13:54:57 -0400
commit28317a087e58dceff85a448c22bbbafebed0b6ab (patch)
tree560f7280284a607abb2f28a801b7ae9a99a14c21 /kqueue.c
parent9556a7d1f647f61634fb782ae7ba814e5a20535e (diff)
downloadlibevent-28317a087e58dceff85a448c22bbbafebed0b6ab.tar.gz
Fix a warn-and-fail bug in kqueue by providing kevent() room to report errors
Apparently, kevent fails gracefully if there is not enough space in its output events array to report every _event_... but it just dies and returns -1 if there is not enough space to report every _error_. There are a couple of possible fixes here. One would to handle -1 returns from kevent better by re-growing the array and retrying... but that seems a little error prone. Instead, I'm just going to say that the events array must be large enough to handle all the errors. This patch also adds a unit test designed to make sure that our many-events-out code works even if not all the events are added at once.
Diffstat (limited to 'kqueue.c')
-rw-r--r--kqueue.c46
1 files changed, 37 insertions, 9 deletions
diff --git a/kqueue.c b/kqueue.c
index 92012982..8b486346 100644
--- a/kqueue.c
+++ b/kqueue.c
@@ -229,6 +229,23 @@ kq_build_changes_list(const struct event_changelist *changelist,
}
static int
+kq_grow_events(struct kqop *kqop, size_t new_size)
+{
+ struct kevent *newresult;
+
+ newresult = mm_realloc(kqop->events,
+ new_size * sizeof(struct kevent));
+
+ if (newresult) {
+ kqop->events = newresult;
+ kqop->events_size = new_size;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static int
kq_dispatch(struct event_base *base, struct timeval *tv)
{
struct kqop *kqop = base->evbase;
@@ -255,6 +272,25 @@ kq_dispatch(struct event_base *base, struct timeval *tv)
changes = kqop->changes;
kqop->changes = NULL;
+ /* Make sure that 'events' is at least as long as the list of changes:
+ * otherwise errors in the changes can get reported as a -1 return
+ * value from kevent() rather than as EV_ERROR events in the events
+ * array.
+ *
+ * (We could instead handle -1 return values from kevent() by
+ * retrying with a smaller changes array or a larger events array,
+ * but this approach seems less risky for now.)
+ */
+ if (kqop->events_size < n_changes) {
+ int new_size = kqop->events_size;
+ do {
+ new_size *= 2;
+ } while (new_size < n_changes);
+
+ kq_grow_events(kqop, new_size);
+ events = kqop->events;
+ }
+
EVBASE_RELEASE_LOCK(base, th_base_lock);
res = kevent(kqop->kq, changes, n_changes,
@@ -319,17 +355,9 @@ kq_dispatch(struct event_base *base, struct timeval *tv)
}
if (res == kqop->events_size) {
- struct kevent *newresult;
- int size = kqop->events_size;
/* We used all the events space that we have. Maybe we should
make it bigger. */
- size *= 2;
- newresult = mm_realloc(kqop->events,
- size * sizeof(struct kevent));
- if (newresult) {
- kqop->events = newresult;
- kqop->events_size = size;
- }
+ kq_grow_events(kqop, kqop->events_size * 2);
}
return (0);