}
/* 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;
{
const struct eventop *evsel;
int res = 0;
- struct event *ev;
int was_notifiable = 0;
int had_signal_added = 0;
/* 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)
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
{
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 */
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);
}
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);
}
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.
*/
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);
}
+
#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.