From: Nick Mathewson Date: Sun, 12 Feb 2012 02:01:53 +0000 (-0500) Subject: Refactor the functions that run over every event. X-Git-Tag: release-2.1.1-alpha~86^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c89b4e63f6b69fa84979ff2aef44c2fdc3e1d8c9;p=libevent Refactor the functions that run over every event. 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. --- diff --git a/event-internal.h b/event-internal.h index c88ab796..5811f2a2 100644 --- a/event-internal.h +++ b/event-internal.h @@ -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 2bb166c5..d8d62256 100644 --- 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); } diff --git a/evmap-internal.h b/evmap-internal.h index 429044ee..d2839a79 100644 --- a/evmap-internal.h +++ b/evmap-internal.h @@ -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 67a3f1a4..568453f4 100644 --- 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); } + diff --git a/util-internal.h b/util-internal.h index d1045e90..b1354416 100644 --- a/util-internal.h +++ b/util-internal.h @@ -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.