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.
This commit is contained in:
Nick Mathewson 2012-02-11 21:01:53 -05:00
parent 604569bf3f
commit c89b4e63f6
5 changed files with 499 additions and 188 deletions

View 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

169
event.c
View File

@ -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);
}
int
event_base_foreach_event_(struct event_base *base,
int (*fn)(struct event_base *, struct event *, void *), void *arg)
{
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) {
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)
{
#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":"");
event_base_foreach_event_(base, dump_inserted_event_fn, output);
}
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":"");
}
}
#endif
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);
}

View 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_ */

404
evmap.c
View File

@ -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)
{
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;
fd = (*mapent)->fd;
#else
for (fd = 0; fd < iomap->nentries; ++fd) {
struct evmap_io *ctx = iomap->entries[fd];
if (!ctx)
continue;
#endif
if ((r = fn(base, fd, ctx, arg)))
break;
}
return r;
}
/* 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;
int r = 0;
int signum;
for (signum = 0; signum < sigmap->nentries; ++signum) {
struct evmap_signal *ctx = sigmap->entries[signum];
if (!ctx)
continue;
if ((r = fn(base, signum, ctx, arg)))
break;
}
return r;
}
/* 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)
{
int res = 0;
evutil_socket_t i;
void *extra;
short events;
const struct eventop *evsel = base->evsel;
struct event_io_map *io = &base->io;
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;
i = (*mapent)->fd;
#else
for (i = 0; i < io->nentries; ++i) {
struct evmap_io *ctx = io->entries[i];
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;
}
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 res;
return 0;
}
int
evmap_signal_reinit(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;
const struct eventop *evsel = base->evsigsel;
int res = 0;
int i;
int *result = arg;
for (i = 0; i < sigmap->nentries; ++i) {
struct evmap_signal *ctx = sigmap->entries[i];
if (!ctx)
continue;
if (!LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, i, 0, EV_SIGNAL, NULL) == -1)
res = -1;
}
if (!LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, signum, 0, EV_SIGNAL, NULL) == -1)
*result = -1;
}
return res;
}
int
evmap_io_delete_all(struct event_base *base)
{
struct event_io_map *io = &base->io;
#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));
}
return 0;
}
int
evmap_signal_delete_all(struct event_base *base)
evmap_reinit(struct event_base *base)
{
struct event_signal_map *sigmap = &base->sigmap;
int i;
int result = 0;
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));
}
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);
}
/* 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)
{
struct event *ev;
int n_read = 0, n_write = 0;
/* 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_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;
}
EVUTIL_ASSERT(n_read == io_info->nread);
EVUTIL_ASSERT(n_write == io_info->nwrite);
return 0;
}
/* 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;
}
void
evmap_check_integrity(struct event_base *base)
{
#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;
#ifdef _EVENT_USE_EVENTLIST
/* XXXXX no-eventlist implementations can use some of this, surely? */
TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
EVUTIL_ASSERT(ev->ev_flags & EVLIST_INIT);
ev->ev_flags &= ~(EVLIST_X_SIGFOUND|EVLIST_X_IOFOUND);
}
#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];
if (!ctx)
continue;
#endif
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++;
}
}
for (i = 0; i < sigmap->nentries; ++i) {
struct evmap_signal *ctx = sigmap->entries[i];
if (!ctx)
continue;
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++;
}
}
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;
}
}
#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. */
evmap_io_foreach_fd(base, evmap_io_check_integrity_fn, NULL);
evmap_signal_foreach_signal(base, evmap_signal_check_integrity_fn, NULL);
if (base->evsel->add == event_changelist_add)
event_changelist_assert_ok(base);
}
/* 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;
}
/* 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;
}
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);
}

View 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.