diff --git a/event-internal.h b/event-internal.h index d8de5761..754ef759 100644 --- a/event-internal.h +++ b/event-internal.h @@ -356,6 +356,14 @@ void event_active_nolock(struct event *ev, int res, short count); void event_base_add_virtual(struct event_base *base); void event_base_del_virtual(struct event_base *base); +/** For debugging: unless assertions are disabled, verify the referential + integrity of the internal data structures of 'base'. This operation can + be expensive. + + Returns on success; aborts on failure. +*/ +void event_base_assert_ok(struct event_base *base); + #ifdef __cplusplus } #endif diff --git a/event.c b/event.c index 575f8f52..79bf6f8a 100644 --- a/event.c +++ b/event.c @@ -864,6 +864,10 @@ event_reinit(struct event_base *base) event_queue_remove_inserted(base, &base->sig.ev_signal); if (base->sig.ev_signal.ev_flags & EVLIST_ACTIVE) event_queue_remove_active(base, &base->sig.ev_signal); + if (base->sig.ev_signal_pair[0] != -1) + EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]); + if (base->sig.ev_signal_pair[1] != -1) + EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]); base->sig.ev_signal_added = 0; } if (base->th_notify_fd[0] != -1) { @@ -899,6 +903,13 @@ event_reinit(struct event_base *base) TAILQ_FOREACH(ev, &base->eventqueue, ev_next) { if (ev->ev_events & (EV_READ|EV_WRITE)) { + if (ev == &base->sig.ev_signal) { + /* If we run into the ev_signal event, it's only + * in eventqueue because some signal event was + * added, which made evsig_add re-add ev_signal. + * So don't double-add it. */ + continue; + } if (evmap_io_add(base, ev->ev_fd, ev) == -1) res = -1; } else if (ev->ev_events & EV_SIGNAL) { @@ -3048,3 +3059,38 @@ event_global_setup_locks_(const int enable_locks) return 0; } #endif + +void +event_base_assert_ok(struct event_base *base) +{ + int i; + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + evmap_check_integrity(base); + + /* Check the heap property */ + for (i = 1; i < (int)base->timeheap.n; ++i) { + int parent = (i - 1) / 2; + 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(evutil_timercmp(&p_ev->ev_timeout, &ev->ev_timeout, <=)); + EVUTIL_ASSERT(ev->ev_timeout_pos.min_heap_idx == i); + } + + /* Check that the common timeouts are fine */ + for (i = 0; i < base->n_common_timeouts; ++i) { + struct common_timeout_list *ctl = base->common_timeout_queues[i]; + struct event *last=NULL, *ev; + 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(is_common_timeout(&ev->ev_timeout,base)); + EVUTIL_ASSERT(COMMON_TIMEOUT_IDX(&ev->ev_timeout) == i); + last = ev; + } + } + + EVBASE_RELEASE_LOCK(base, th_base_lock); +} diff --git a/evmap-internal.h b/evmap-internal.h index fde661d1..8bf655fd 100644 --- a/evmap-internal.h +++ b/evmap-internal.h @@ -87,4 +87,6 @@ void evmap_signal_active(struct event_base *base, evutil_socket_t signum, int nc void *evmap_io_get_fdinfo(struct event_io_map *ctx, evutil_socket_t fd); +void evmap_check_integrity(struct event_base *base); + #endif /* _EVMAP_H_ */ diff --git a/evmap.c b/evmap.c index db98e0a1..b69c5d84 100644 --- a/evmap.c +++ b/evmap.c @@ -721,3 +721,66 @@ event_changelist_del(struct event_base *base, evutil_socket_t fd, short old, sho return (0); } +void +evmap_check_integrity(struct event_base *base) +{ +#define EVLIST_X_SIGFOUND 0x1000 +#define EVLIST_X_IOFOUND 0x2000 + + int i; + struct event *ev; + struct event_io_map *io = &base->io; + struct event_signal_map *sigmap = &base->sigmap; + int nsignals, ntimers, nio; + nsignals = ntimers = nio = 0; + + 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); + } + + + for (i = 0; i < io->nentries; ++i) { + struct evmap_io *ctx = io->entries[i]; + if (!ctx) + continue; + + 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; + } + } + + EVUTIL_ASSERT(nio == 0); + EVUTIL_ASSERT(nsignals == 0); + /* There is no "EVUTIL_ASSERT(ntimers == 0)": eventqueue is only for + * pending signals and io events. + */ +} diff --git a/test/regress.c b/test/regress.c index 1d2d2dcc..0371e2b7 100644 --- a/test/regress.c +++ b/test/regress.c @@ -677,9 +677,11 @@ test_persistent_active_timeout(void *ptr) tv_exit.tv_usec = 600 * 1000; event_base_loopexit(base, &tv_exit); + event_base_assert_ok(base); evutil_gettimeofday(&start, NULL); event_base_dispatch(base); + event_base_assert_ok(base); tt_int_op(res.n, ==, 3); tt_int_op(res.events[0], ==, EV_READ); @@ -748,9 +750,11 @@ test_common_timeout(void *ptr) } } + event_base_assert_ok(base); event_base_dispatch(base); evutil_gettimeofday(&now, NULL); + event_base_assert_ok(base); for (i=0; i<10; ++i) { struct timeval tmp; @@ -824,12 +828,19 @@ test_fork(void) evsignal_set(&sig_ev, SIGCHLD, child_signal_cb, &got_sigchld); evsignal_add(&sig_ev, NULL); + event_base_assert_ok(current_base); + TT_BLATHER(("Before fork")); if ((pid = fork()) == 0) { /* in the child */ + TT_BLATHER(("In child, before reinit")); + event_base_assert_ok(current_base); if (event_reinit(current_base) == -1) { fprintf(stdout, "FAILED (reinit)\n"); exit(1); } + TT_BLATHER(("After reinit")); + event_base_assert_ok(current_base); + TT_BLATHER(("After assert-ok")); evsignal_del(&sig_ev); @@ -852,10 +863,12 @@ test_fork(void) tt_fail_perror("write"); } + TT_BLATHER(("Before waitpid")); if (waitpid(pid, &status, 0) == -1) { fprintf(stdout, "FAILED (fork)\n"); exit(1); } + TT_BLATHER(("After waitpid")); if (WEXITSTATUS(status) != 76) { fprintf(stdout, "FAILED (exit): %d\n", WEXITSTATUS(status)); diff --git a/test/regress_main.c b/test/regress_main.c index ce623112..26df81fb 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -76,6 +76,7 @@ #include "tinytest.h" #include "tinytest_macros.h" #include "../iocp-internal.h" +#include "../event-internal.h" long timeval_msec_diff(const struct timeval *start, const struct timeval *end) @@ -257,8 +258,10 @@ basic_test_cleanup(const struct testcase_t *testcase, void *ptr) } if (testcase->flags & TT_NEED_BASE) { - if (data->base) + if (data->base) { + event_base_assert_ok(data->base); event_base_free(data->base); + } } free(data);