]> granicus.if.org Git - libevent/commitdiff
Refactor the functions that run over every event.
authorNick Mathewson <nickm@torproject.org>
Sun, 12 Feb 2012 02:01:53 +0000 (21:01 -0500)
committerNick Mathewson <nickm@torproject.org>
Mon, 20 Feb 2012 18:55:23 +0000 (13:55 -0500)
Now there are appropriate "for each event", "for each fd", and "for
each signal" helpers that they can use; this makes the code a bit
simpler, and far less duplicated.

This lets me turn back on the functions I disabled when removing
eventlist.

Additionally, check more lists for circularity in
event_base_assert_ok().

Add typedefs for the callback types.

Name fewer things "ctx".

Adds an implementation of Floyd's tortoise-and-hare algorithm to check
for circularity in TAILQs and LISTs, to avoid the abuse of flags that
event_base_assert_ok() was doing before.

Suggested by Dave Hart.

event-internal.h
event.c
evmap-internal.h
evmap.c
util-internal.h

index c88ab7965c780559cbfde24edbbc9d086f869121..5811f2a29bb4d837c471672b256009d03137dc89 100644 (file)
@@ -366,6 +366,20 @@ void event_base_del_virtual(struct event_base *base);
 */
 void event_base_assert_ok(struct event_base *base);
 
+/* Callback type for event_base_foreach_event. */
+typedef int (*event_base_foreach_event_cb)(struct event_base *base, struct event *, void *);
+
+/* Helper function: Call 'fn' exactly once every inserted or active event in
+ * the event_base 'base'.
+ *
+ * If fn returns 0, continue on to the next event. Otherwise, return the same
+ * value that fn returned.
+ *
+ * Requires that 'base' be locked.
+ */
+int event_base_foreach_event_(struct event_base *base,
+    event_base_foreach_event_cb cb, void *arg);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/event.c b/event.c
index 2bb166c5b2ed71b78d489f133df8ea6d5a4f840c..d8d62256cbd92a9d0faa40c293bdc02511bdf1dc 100644 (file)
--- a/event.c
+++ b/event.c
@@ -766,8 +766,8 @@ event_base_free(struct event_base *base)
        }
 
        /* Delete all non-internal events. */
-       evmap_signal_delete_all(base);
-       evmap_io_delete_all(base);
+       evmap_delete_all(base);
+
        while ((ev = min_heap_top(&base->timeheap)) != NULL) {
                event_del(ev);
                ++n_deleted;
@@ -856,7 +856,6 @@ event_reinit(struct event_base *base)
 {
        const struct eventop *evsel;
        int res = 0;
-       struct event *ev;
        int was_notifiable = 0;
        int had_signal_added = 0;
 
@@ -937,9 +936,7 @@ event_reinit(struct event_base *base)
                /* Tell the event maps to re-inform the backend about all
                 * pending events. This will make the signal notification
                 * event get re-created if necessary. */
-               if (evmap_io_reinit(base) < 0)
-                       res = -1;
-               if (evmap_signal_reinit(base) < 0)
+               if (evmap_reinit(base) < 0)
                        res = -1;
        } else {
                if (had_signal_added)
@@ -3029,38 +3026,125 @@ evthread_make_base_notifiable(struct event_base *base)
        return event_add(&base->th_notify, NULL);
 }
 
-void
-event_base_dump_events(struct event_base *base, FILE *output)
+int
+event_base_foreach_event_(struct event_base *base,
+    int (*fn)(struct event_base *, struct event *, void *), void *arg)
 {
-#ifdef _EVENT_USE_EVENTLIST
-       /* re-enable XXXXXX */
-       struct event *e;
-       int i;
-       fprintf(output, "Inserted events:\n");
-       TAILQ_FOREACH(e, &base->eventqueue, ev_next) {
-               fprintf(output, "  %p [fd %ld]%s%s%s%s%s\n",
-                               (void*)e, (long)e->ev_fd,
-                               (e->ev_events&EV_READ)?" Read":"",
-                               (e->ev_events&EV_WRITE)?" Write":"",
-                               (e->ev_events&EV_SIGNAL)?" Signal":"",
-                               (e->ev_events&EV_TIMEOUT)?" Timeout":"",
-                               (e->ev_events&EV_PERSIST)?" Persist":"");
+       int r, i;
+       unsigned u;
+       struct event *ev;
 
+       /* Start out with all the EVLIST_INSERTED events. */
+       if ((r = evmap_foreach_event(base, fn, arg)))
+               return r;
+
+       /* Okay, now we deal with those events that have timeouts and are in
+        * the min-heap. */
+       for (u = 0; u < base->timeheap.n; ++u) {
+               ev = base->timeheap.p[u];
+               if (ev->ev_flags & EVLIST_INSERTED) {
+                       /* we already processed this one */
+                       continue;
+               }
+               if ((r = fn(base, ev, arg)))
+                       return r;
        }
+
+       /* Now for the events in one of the timeout queues.
+        * the min-heap. */
+       for (i = 0; i < base->n_common_timeouts; ++i) {
+               struct common_timeout_list *ctl =
+                   base->common_timeout_queues[i];
+               TAILQ_FOREACH(ev, &ctl->events,
+                   ev_timeout_pos.ev_next_with_common_timeout) {
+                       if (ev->ev_flags & EVLIST_INSERTED) {
+                               /* we already processed this one */
+                               continue;
+                       }
+                       if ((r = fn(base, ev, arg)))
+                               return r;
+               }
+       }
+
+       /* Finally, we deal wit all the active events that we haven't touched
+        * yet. */
        for (i = 0; i < base->nactivequeues; ++i) {
-               if (TAILQ_EMPTY(&base->activequeues[i]))
-                       continue;
-               fprintf(output, "Active events [priority %d]:\n", i);
-               TAILQ_FOREACH(e, &base->eventqueue, ev_next) {
-                       fprintf(output, "  %p [fd %ld]%s%s%s%s\n",
-                                       (void*)e, (long)e->ev_fd,
-                                       (e->ev_res&EV_READ)?" Read active":"",
-                                       (e->ev_res&EV_WRITE)?" Write active":"",
-                                       (e->ev_res&EV_SIGNAL)?" Signal active":"",
-                                       (e->ev_res&EV_TIMEOUT)?" Timeout active":"");
+               TAILQ_FOREACH(ev, &base->activequeues[i], ev_active_next) {
+                       if (ev->ev_flags & (EVLIST_INSERTED|EVLIST_TIMEOUT)) {
+                               /* we already processed this one */
+                               continue;
+                       }
+                       if ((r = fn(base, ev, arg)))
+                               return r;
                }
        }
+
+       return 0;
+}
+
+/* Helper for event_base_dump_events: called on each event in the event base;
+ * dumps only the inserted events. */
+static int
+dump_inserted_event_fn(struct event_base *base, struct event *e, void *arg)
+{
+       FILE *output = arg;
+       const char *gloss = (e->ev_events & EV_SIGNAL) ?
+           "sig" : "fd ";
+
+       if (! (e->ev_flags & (EVLIST_INSERTED|EVLIST_TIMEOUT)))
+               return 0;
+
+       fprintf(output, "  %p [%s %ld]%s%s%s%s",
+           (void*)e, gloss, (long)e->ev_fd,
+           (e->ev_events&EV_READ)?" Read":"",
+           (e->ev_events&EV_WRITE)?" Write":"",
+           (e->ev_events&EV_SIGNAL)?" Signal":"",
+           (e->ev_events&EV_PERSIST)?" Persist":"");
+       if (e->ev_flags & EVLIST_TIMEOUT) {
+               struct timeval tv;
+               tv.tv_sec = e->ev_timeout.tv_sec;
+               tv.tv_usec = e->ev_timeout.tv_usec & MICROSECONDS_MASK;
+#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+               evutil_timeradd(&tv, &base->tv_clock_diff, &tv);
 #endif
+               fprintf(output, " Timeout=%ld.%06d",
+                   (long)tv.tv_sec, (int)(tv.tv_usec & MICROSECONDS_MASK));
+       }
+       fputc('\n', output);
+
+       return 0;
+}
+
+/* Helper for event_base_dump_events: called on each event in the event base;
+ * dumps only the active events. */
+static int
+dump_active_event_fn(struct event_base *base, struct event *e, void *arg)
+{
+       FILE *output = arg;
+       const char *gloss = (e->ev_events & EV_SIGNAL) ?
+           "sig" : "fd ";
+
+       if (! (e->ev_flags & EVLIST_ACTIVE))
+               return 0;
+
+       fprintf(output, "  %p [%s %ld, priority=%d]%s%s%s%s\n",
+           (void*)e, gloss, (long)e->ev_fd, e->ev_pri,
+           (e->ev_res&EV_READ)?" Read active":"",
+           (e->ev_res&EV_WRITE)?" Write active":"",
+           (e->ev_res&EV_SIGNAL)?" Signal active":"",
+           (e->ev_res&EV_TIMEOUT)?" Timeout active":"");
+
+       return 0;
+}
+
+void
+event_base_dump_events(struct event_base *base, FILE *output)
+{
+       fprintf(output, "Inserted events:\n");
+       event_base_foreach_event_(base, dump_inserted_event_fn, output);
+
+       fprintf(output, "Active events:\n");
+       event_base_foreach_event_(base, dump_active_event_fn, output);
 }
 
 void
@@ -3102,6 +3186,8 @@ event_base_assert_ok(struct event_base *base)
 {
        int i;
        EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+
+       /* First do checks on the per-fd and per-signal lists */
        evmap_check_integrity(base);
 
        /* Check the heap property */
@@ -3110,7 +3196,7 @@ event_base_assert_ok(struct event_base *base)
                struct event *ev, *p_ev;
                ev = base->timeheap.p[i];
                p_ev = base->timeheap.p[parent];
-               EVUTIL_ASSERT(ev->ev_flags & EV_TIMEOUT);
+               EVUTIL_ASSERT(ev->ev_flags & EVLIST_TIMEOUT);
                EVUTIL_ASSERT(evutil_timercmp(&p_ev->ev_timeout, &ev->ev_timeout, <=));
                EVUTIL_ASSERT(ev->ev_timeout_pos.min_heap_idx == i);
        }
@@ -3119,15 +3205,28 @@ event_base_assert_ok(struct event_base *base)
        for (i = 0; i < base->n_common_timeouts; ++i) {
                struct common_timeout_list *ctl = base->common_timeout_queues[i];
                struct event *last=NULL, *ev;
+
+               EVUTIL_ASSERT_TAILQ_OK(&ctl->events, event, ev_timeout_pos.ev_next_with_common_timeout);
+
                TAILQ_FOREACH(ev, &ctl->events, ev_timeout_pos.ev_next_with_common_timeout) {
                        if (last)
                                EVUTIL_ASSERT(evutil_timercmp(&last->ev_timeout, &ev->ev_timeout, <=));
-                       EVUTIL_ASSERT(ev->ev_flags & EV_TIMEOUT);
+                       EVUTIL_ASSERT(ev->ev_flags & EVLIST_TIMEOUT);
                        EVUTIL_ASSERT(is_common_timeout(&ev->ev_timeout,base));
                        EVUTIL_ASSERT(COMMON_TIMEOUT_IDX(&ev->ev_timeout) == i);
                        last = ev;
                }
        }
 
+       /* Check the active queues. */
+       for (i = 0; i < base->nactivequeues; ++i) {
+               struct event *ev;
+               EVUTIL_ASSERT_TAILQ_OK(&base->activequeues[i], event, ev_active_next);
+               TAILQ_FOREACH(ev, &base->activequeues[i], ev_active_next) {
+                       EVUTIL_ASSERT(ev->ev_pri == i);
+                       EVUTIL_ASSERT(ev->ev_flags & EVLIST_ACTIVE);
+               }
+       }
+
        EVBASE_RELEASE_LOCK(base, th_base_lock);
 }
index 429044ee0f9aa7e78ef8e83029d2021b17740d0e..d2839a7923793952ef76a9d9cd301a3c25d6a5f2 100644 (file)
@@ -85,14 +85,33 @@ int evmap_signal_add(struct event_base *base, int signum, struct event *ev);
 int evmap_signal_del(struct event_base *base, int signum, struct event *ev);
 void evmap_signal_active(struct event_base *base, evutil_socket_t signum, int ncalls);
 
+/* Return the fdinfo object associated with a given fd.  If the fd has no
+ * events associated with it, the result may be NULL.
+ */
 void *evmap_io_get_fdinfo(struct event_io_map *ctx, evutil_socket_t fd);
 
-int evmap_io_reinit(struct event_base *base);
-int evmap_signal_reinit(struct event_base *base);
+/* Helper for event_reinit(): Tell the backend to re-add every fd and signal
+ * for which we have a pending event.
+ */
+int evmap_reinit(struct event_base *base);
 
-int evmap_io_delete_all(struct event_base *base);
-int evmap_signal_delete_all(struct event_base *base);
+/* Helper for event_base_free(): Call event_del() on every pending fd and
+ * signal event.
+ */
+void evmap_delete_all(struct event_base *base);
 
+/* Helper for event_base_assert_ok(): Check referential integrity of the
+ * evmaps.
+ */
 void evmap_check_integrity(struct event_base *base);
 
+/* Helper: Call fn on every fd or signal event, passing as its arguments the
+ * provided event_base, the event, and arg.  If fn returns 0, process the next
+ * event.  If it returns any other value, return that value and process no
+ * more events.
+ */
+int evmap_foreach_event(struct event_base *base,
+    event_base_foreach_event_cb fn,
+    void *arg);
+
 #endif /* _EVMAP_H_ */
diff --git a/evmap.c b/evmap.c
index 67a3f1a4cd9f0565b0b4598a6fd9d4bf6fa9aaae..568453f42e8608aad8b9ce0dde90ab8a7308fd33 100644 (file)
--- a/evmap.c
+++ b/evmap.c
@@ -488,104 +488,175 @@ evmap_io_get_fdinfo(struct event_io_map *map, evutil_socket_t fd)
                return NULL;
 }
 
-int
-evmap_io_reinit(struct event_base *base)
+/* Callback type for evmap_io_foreach_fd */
+typedef int (*evmap_io_foreach_fd_cb)(
+       struct event_base *, evutil_socket_t, struct evmap_io *, void *);
+
+/* Multipurpose helper function: Iterate over every file descriptor event_base
+ * for which we could have EV_READ or EV_WRITE events.  For each such fd, call
+ * fn(base, signum, evmap_io, arg), where fn is the user-provided
+ * function, base is the event_base, signum is the signal number, evmap_io
+ * is an evmap_io structure containing a list of events pending on the
+ * file descriptor, and arg is the user-supplied argument.
+ *
+ * If fn returns 0, continue on to the next signal. Otherwise, return the same
+ * value that fn returned.
+ *
+ * Note that there is no guarantee that the file descriptors will be processed
+ * in any particular order.
+ */
+static int
+evmap_io_foreach_fd(struct event_base *base,
+    evmap_io_foreach_fd_cb fn,
+    void *arg)
 {
-       int res = 0;
-       evutil_socket_t i;
-       void *extra;
-       short events;
-       const struct eventop *evsel = base->evsel;
-       struct event_io_map *io = &base->io;
-
+       evutil_socket_t fd;
+       struct event_io_map *iomap = &base->io;
+       int r = 0;
 #ifdef EVMAP_USE_HT
        struct event_map_entry **mapent;
        HT_FOREACH(mapent, event_io_map, io) {
                struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
-               i = (*mapent)->fd;
+               fd = (*mapent)->fd;
 #else
-       for (i = 0; i < io->nentries; ++i) {
-               struct evmap_io *ctx = io->entries[i];
+       for (fd = 0; fd < iomap->nentries; ++fd) {
+               struct evmap_io *ctx = iomap->entries[fd];
                if (!ctx)
                        continue;
 #endif
-               events = 0;
-               extra = ((char*)ctx) + sizeof(struct evmap_io);
-               if (ctx->nread)
-                       events |= EV_READ;
-               if (ctx->nread)
-                       events |= EV_WRITE;
-               if (evsel->fdinfo_len)
-                       memset(extra, 0, evsel->fdinfo_len);
-               if (events && LIST_FIRST(&ctx->events) &&
-                   (LIST_FIRST(&ctx->events)->ev_events & EV_ET))
-                       events |= EV_ET;
-               if (evsel->add(base, i, 0, events, extra) == -1)
-                       res = -1;
+               if ((r = fn(base, fd, ctx, arg)))
+                       break;
        }
-
-       return res;
+       return r;
 }
 
-int
-evmap_signal_reinit(struct event_base *base)
+/* Callback type for evmap_signal_foreach_signal */
+typedef int (*evmap_signal_foreach_signal_cb)(
+       struct event_base *, int, struct evmap_signal *, void *);
+
+/* Multipurpose helper function: Iterate over every signal number in the
+ * event_base for which we could have signal events.  For each such signal,
+ * call fn(base, signum, evmap_signal, arg), where fn is the user-provided
+ * function, base is the event_base, signum is the signal number, evmap_signal
+ * is an evmap_signal structure containing a list of events pending on the
+ * signal, and arg is the user-supplied argument.
+ *
+ * If fn returns 0, continue on to the next signal. Otherwise, return the same
+ * value that fn returned.
+ */
+static int
+evmap_signal_foreach_signal(struct event_base *base,
+    evmap_signal_foreach_signal_cb fn,
+    void *arg)
 {
        struct event_signal_map *sigmap = &base->sigmap;
-       const struct eventop *evsel = base->evsigsel;
-       int res = 0;
-       int i;
+       int r = 0;
+       int signum;
 
-       for (i = 0; i < sigmap->nentries; ++i) {
-               struct evmap_signal *ctx = sigmap->entries[i];
+       for (signum = 0; signum < sigmap->nentries; ++signum) {
+               struct evmap_signal *ctx = sigmap->entries[signum];
                if (!ctx)
                        continue;
-               if (!LIST_EMPTY(&ctx->events)) {
-                       if (evsel->add(base, i, 0, EV_SIGNAL, NULL) == -1)
-                               res = -1;
-               }
+               if ((r = fn(base, signum, ctx, arg)))
+                       break;
        }
-       return res;
+       return r;
 }
 
-int
-evmap_io_delete_all(struct event_base *base)
+/* Helper for evmap_reinit: tell the backend to add every fd for which we have
+ * pending events, with the appropriate combination of EV_READ, EV_WRITE, and
+ * EV_ET. */
+static int
+evmap_io_reinit_iter_fn(struct event_base *base, evutil_socket_t fd,
+    struct evmap_io *ctx, void *arg)
 {
-       struct event_io_map *io = &base->io;
+       const struct eventop *evsel = base->evsel;
+       void *extra;
+       int *result = arg;
+       short events = 0;
+       struct event *ev;
+       EVUTIL_ASSERT(ctx);
 
-#ifdef EVMAP_USE_HT
-       struct event_map_entry **mapent;
-       HT_FOREACH(mapent, event_io_map, io) {
-               struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
-#else
-       evutil_socket_t i;
-       for (i = 0; i < io->nentries; ++i) {
-               struct evmap_io *ctx = io->entries[i];
-               if (!ctx)
-                       continue;
-#endif
-               while (LIST_FIRST(&ctx->events))
-                       event_del(LIST_FIRST(&ctx->events));
-       }
+       extra = ((char*)ctx) + sizeof(struct evmap_io);
+       if (ctx->nread)
+               events |= EV_READ;
+       if (ctx->nread)
+               events |= EV_WRITE;
+       if (evsel->fdinfo_len)
+               memset(extra, 0, evsel->fdinfo_len);
+       if (events &&
+           (ev = LIST_FIRST(&ctx->events)) &&
+           (ev->ev_events & EV_ET))
+               events |= EV_ET;
+       if (evsel->add(base, fd, 0, events, extra) == -1)
+               *result = -1;
 
        return 0;
 }
 
-int
-evmap_signal_delete_all(struct event_base *base)
+/* Helper for evmap_reinit: tell the backend to add every signal for which we
+ * have pending events.  */
+static int
+evmap_signal_reinit_iter_fn(struct event_base *base,
+    int signum, struct evmap_signal *ctx, void *arg)
 {
-       struct event_signal_map *sigmap = &base->sigmap;
-       int i;
+       const struct eventop *evsel = base->evsigsel;
+       int *result = arg;
 
-       for (i = 0; i < sigmap->nentries; ++i) {
-               struct evmap_signal *ctx = sigmap->entries[i];
-               if (!ctx)
-                       continue;
-               while (!LIST_EMPTY(&ctx->events))
-                       event_del(LIST_FIRST(&ctx->events));
+       if (!LIST_EMPTY(&ctx->events)) {
+               if (evsel->add(base, signum, 0, EV_SIGNAL, NULL) == -1)
+                       *result = -1;
        }
        return 0;
 }
 
+int
+evmap_reinit(struct event_base *base)
+{
+       int result = 0;
+
+       evmap_io_foreach_fd(base, evmap_io_reinit_iter_fn, &result);
+       if (result < 0)
+               return -1;
+       evmap_signal_foreach_signal(base, evmap_signal_reinit_iter_fn, &result);
+       if (result < 0)
+               return -1;
+       return 0;
+}
+
+/* Helper for evmap_delete_all: delete every event in an event_dlist. */
+static int
+delete_all_in_dlist(struct event_dlist *dlist)
+{
+       struct event *ev;
+       while ((ev = LIST_FIRST(dlist)))
+               event_del(ev);
+       return 0;
+}
+
+/* Helper for evmap_delete_all: delete every event pending on an fd. */
+static int
+evmap_io_delete_all_iter_fn(struct event_base *base, evutil_socket_t fd,
+    struct evmap_io *io_info, void *arg)
+{
+       return delete_all_in_dlist(&io_info->events);
+}
+
+/* Helper for evmap_delete_all: delete every event pending on a signal. */
+static int
+evmap_signal_delete_all_iter_fn(struct event_base *base, int signum,
+    struct evmap_signal *sig_info, void *arg)
+{
+       return delete_all_in_dlist(&sig_info->events);
+}
+
+void
+evmap_delete_all(struct event_base *base)
+{
+       evmap_signal_foreach_signal(base, evmap_signal_delete_all_iter_fn, NULL);
+       evmap_io_foreach_fd(base, evmap_io_delete_all_iter_fn, NULL);
+}
+
 /** Per-fd structure for use with changelists.  It keeps track, for each fd or
  * signal using the changelist, of where its entry in the changelist is.
  */
@@ -821,80 +892,115 @@ event_changelist_del(struct event_base *base, evutil_socket_t fd, short old, sho
        return (0);
 }
 
-void
-evmap_check_integrity(struct event_base *base)
+/* Helper for evmap_check_integrity: verify that all of the events pending on
+ * given fd are set up correctly, and that the nread and nwrite counts on that
+ * fd are correct. */
+static int
+evmap_io_check_integrity_fn(struct event_base *base, evutil_socket_t fd,
+    struct evmap_io *io_info, void *arg)
 {
-#define EVLIST_X_SIGFOUND 0x1000
-#define EVLIST_X_IOFOUND 0x2000
-
-       evutil_socket_t i;
        struct event *ev;
-       struct event_io_map *io = &base->io;
-       struct event_signal_map *sigmap = &base->sigmap;
-#ifdef EVMAP_USE_HT
-       struct event_map_entry **mapent;
-#endif
-       int nsignals, ntimers, nio;
-       nsignals = ntimers = nio = 0;
+       int n_read = 0, n_write = 0;
 
-#ifdef _EVENT_USE_EVENTLIST
-       /* XXXXX no-eventlist implementations can use some of this, surely? */
-       TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
+       /* First, make sure the list itself isn't corrupt. Otherwise,
+        * running LIST_FOREACH could be an exciting adventure. */
+       EVUTIL_ASSERT_LIST_OK(&io_info->events, event, ev_io_next);
+
+       LIST_FOREACH(ev, &io_info->events, ev_io_next) {
                EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
-               EVUTIL_ASSERT(ev->ev_flags & EVLIST_INIT);
-               ev->ev_flags &= ~(EVLIST_X_SIGFOUND|EVLIST_X_IOFOUND);
+               EVUTIL_ASSERT(ev->ev_fd == fd);
+               EVUTIL_ASSERT(!(ev->ev_events & EV_SIGNAL));
+               EVUTIL_ASSERT((ev->ev_events & (EV_READ|EV_WRITE)));
+               if (ev->ev_events & EV_READ)
+                       ++n_read;
+               if (ev->ev_events & EV_WRITE)
+                       ++n_write;
        }
 
-#ifdef EVMAP_USE_HT
-       HT_FOREACH(mapent, event_io_map, io) {
-               struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
-               i = (*mapent)->fd;
-#else
-       for (i = 0; i < io->nentries; ++i) {
-               struct evmap_io *ctx = io->entries[i];
+       EVUTIL_ASSERT(n_read == io_info->nread);
+       EVUTIL_ASSERT(n_write == io_info->nwrite);
 
-               if (!ctx)
-                       continue;
-#endif
+       return 0;
+}
 
-               LIST_FOREACH(ev, &ctx->events, ev_io_next) {
-                       EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_IOFOUND));
-                       EVUTIL_ASSERT(ev->ev_fd == i);
-                       ev->ev_flags |= EVLIST_X_IOFOUND;
-                       nio++;
-               }
+/* Helper for evmap_check_integrity: verify that all of the events pending
+ * on given signal are set up correctly. */
+static int
+evmap_signal_check_integrity_fn(struct event_base *base,
+    int signum, struct evmap_signal *sig_info, void *arg)
+{
+       struct event *ev;
+       /* First, make sure the list itself isn't corrupt. */
+       EVUTIL_ASSERT_LIST_OK(&sig_info->events, event, ev_signal_next);
+
+       LIST_FOREACH(ev, &sig_info->events, ev_io_next) {
+               EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
+               EVUTIL_ASSERT(ev->ev_fd == signum);
+               EVUTIL_ASSERT((ev->ev_events & EV_SIGNAL));
+               EVUTIL_ASSERT(!(ev->ev_events & (EV_READ|EV_WRITE)));
        }
+       return 0;
+}
 
-       for (i = 0; i < sigmap->nentries; ++i) {
-               struct evmap_signal *ctx = sigmap->entries[i];
-               if (!ctx)
-                       continue;
+void
+evmap_check_integrity(struct event_base *base)
+{
+       evmap_io_foreach_fd(base, evmap_io_check_integrity_fn, NULL);
+       evmap_signal_foreach_signal(base, evmap_signal_check_integrity_fn, NULL);
 
-               LIST_FOREACH(ev, &ctx->events, ev_signal_next) {
-                       EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_SIGFOUND));
-                       EVUTIL_ASSERT(ev->ev_fd == i);
-                       ev->ev_flags |= EVLIST_X_SIGFOUND;
-                       nsignals++;
-               }
-       }
+       if (base->evsel->add == event_changelist_add)
+               event_changelist_assert_ok(base);
+}
 
-       TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
-               if (ev->ev_events & (EV_READ|EV_WRITE)) {
-                       EVUTIL_ASSERT(ev->ev_flags & EVLIST_X_IOFOUND);
-                       --nio;
-               }
-               if (ev->ev_events & EV_SIGNAL) {
-                       EVUTIL_ASSERT(ev->ev_flags & EVLIST_X_SIGFOUND);
-                       --nsignals;
-               }
+/* Helper type for evmap_foreach_event: Bundles a function to call on every
+ * event, and the user-provided void* to use as its third argument. */
+struct evmap_foreach_event_helper {
+       int (*fn)(struct event_base *, struct event *, void *);
+       void *arg;
+};
+
+/* Helper for evmap_foreach_event: calls a provided function on every event
+ * pending on a given fd.  */
+static int
+evmap_io_foreach_event_fn(struct event_base *base, evutil_socket_t fd,
+    struct evmap_io *io_info, void *arg)
+{
+       struct evmap_foreach_event_helper *h = arg;
+       struct event *ev;
+       int r;
+       LIST_FOREACH(ev, &io_info->events, ev_io_next) {
+               if ((r = h->fn(base, ev, h->arg)))
+                       return r;
        }
+       return 0;
+}
 
-#endif
-       EVUTIL_ASSERT(nio == 0);
-       EVUTIL_ASSERT(nsignals == 0);
-       /* There is no "EVUTIL_ASSERT(ntimers == 0)": eventqueue is only for
-        * pending signals and io events. */
+/* Helper for evmap_foreach_event: calls a provided function on every event
+ * pending on a given signal.  */
+static int
+evmap_signal_foreach_event_fn(struct event_base *base, int signum,
+    struct evmap_signal *sig_info, void *arg)
+{
+       struct event *ev;
+       struct evmap_foreach_event_helper *h = arg;
+       int r;
+       LIST_FOREACH(ev, &sig_info->events, ev_signal_next) {
+               if ((r = h->fn(base, ev, h->arg)))
+                       return r;
+       }
+       return 0;
+}
 
-       if (base->evsel->add == event_changelist_add)
-               event_changelist_assert_ok(base);
+int
+evmap_foreach_event(struct event_base *base,
+    int (*fn)(struct event_base *, struct event *, void *), void *arg)
+{
+       struct evmap_foreach_event_helper h;
+       int r;
+       h.fn = fn;
+       h.arg = arg;
+       if ((r = evmap_io_foreach_fd(base, evmap_io_foreach_event_fn, &h)))
+               return r;
+       return evmap_signal_foreach_signal(base, evmap_signal_foreach_event_fn, &h);
 }
+
index d1045e90b163f8ac16bf59da4d95880ba13a773d..b13544168eba81c6158249b894507ebcd0501159 100644 (file)
@@ -130,6 +130,79 @@ extern "C" {
 #define EVUTIL_SHUT_BOTH 2
 #endif
 
+/* Helper: Verify that all the elements in 'dlist' are internally consistent.
+ * Checks for circular lists and bad prev/next pointers.
+ *
+ * Example usage:
+ *    EVUTIL_ASSERT_LIST_OK(eventlist, event, ev_next);
+ */
+#define EVUTIL_ASSERT_LIST_OK(dlist, type, field) do {                 \
+               struct type *elm1, *elm2, **nextp;                      \
+               if (LIST_EMPTY((dlist)))                                \
+                       break;                                          \
+                                                                       \
+               /* Check list for circularity using Floyd's */          \
+               /* 'Tortoise and Hare' algorithm */                     \
+               elm1 = LIST_FIRST((dlist));                             \
+               elm2 = LIST_NEXT(elm1, field);                          \
+               while (elm1 && elm2) {                                  \
+                       EVUTIL_ASSERT(elm1 != elm2);                    \
+                       elm1 = LIST_NEXT(elm1, field);                  \
+                       elm2 = LIST_NEXT(elm2, field);                  \
+                       if (!elm2)                                      \
+                               break;                                  \
+                       EVUTIL_ASSERT(elm1 != elm2);                    \
+                       elm2 = LIST_NEXT(elm2, field);                  \
+               }                                                       \
+                                                                       \
+               /* Now check next and prev pointers for consistency. */ \
+               nextp = &LIST_FIRST((dlist));                           \
+               elm1 = LIST_FIRST((dlist));                             \
+               while (elm1) {                                          \
+                       EVUTIL_ASSERT(*nextp == elm1);                  \
+                       EVUTIL_ASSERT(nextp == elm1->field.le_prev);    \
+                       nextp = &LIST_NEXT(elm1, field);                \
+                       elm1 = *nextp;                                  \
+               }                                                       \
+       } while (0)
+
+/* Helper: Verify that all the elements in a TAILQ are internally consistent.
+ * Checks for circular lists and bad prev/next pointers.
+ *
+ * Example usage:
+ *    EVUTIL_ASSERT_TAILQ_OK(activelist, event, ev_active_next);
+ */
+#define EVUTIL_ASSERT_TAILQ_OK(tailq, type, field) do {                        \
+               struct type *elm1, *elm2, **nextp;                      \
+               if (TAILQ_EMPTY((tailq)))                               \
+                       break;                                          \
+                                                                       \
+               /* Check list for circularity using Floyd's */          \
+               /* 'Tortoise and Hare' algorithm */                     \
+               elm1 = TAILQ_FIRST((tailq));                            \
+               elm2 = TAILQ_NEXT(elm1, field);                         \
+               while (elm1 && elm2) {                                  \
+                       EVUTIL_ASSERT(elm1 != elm2);                    \
+                       elm1 = TAILQ_NEXT(elm1, field);                 \
+                       elm2 = TAILQ_NEXT(elm2, field);                 \
+                       if (!elm2)                                      \
+                               break;                                  \
+                       EVUTIL_ASSERT(elm1 != elm2);                    \
+                       elm2 = TAILQ_NEXT(elm2, field);                 \
+               }                                                       \
+                                                                       \
+               /* Now check next and prev pointers for consistency. */ \
+               nextp = &TAILQ_FIRST((tailq));                          \
+               elm1 = TAILQ_FIRST((tailq));                            \
+               while (elm1) {                                          \
+                       EVUTIL_ASSERT(*nextp == elm1);                  \
+                       EVUTIL_ASSERT(nextp == elm1->field.tqe_prev);   \
+                       nextp = &TAILQ_NEXT(elm1, field);               \
+                       elm1 = *nextp;                                  \
+               }                                                       \
+               EVUTIL_ASSERT(nextp == (tailq)->tqh_last);              \
+       } while (0)
+
 /* Locale-independent replacements for some ctypes functions.  Use these
  * when you care about ASCII's notion of character types, because you are about
  * to send those types onto the wire.