mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-09 12:28:19 -04:00
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:
parent
604569bf3f
commit
c89b4e63f6
@ -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
169
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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
404
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user