1/*
2 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26#include "event2/event-config.h"
27
28#ifdef WIN32
29#include <winsock2.h>
30#define WIN32_LEAN_AND_MEAN
31#include <windows.h>
32#undef WIN32_LEAN_AND_MEAN
33#endif
34#include <sys/types.h>
35#if !defined(WIN32) && defined(_EVENT_HAVE_SYS_TIME_H)
36#include <sys/time.h>
37#endif
38#include <sys/queue.h>
39#include <stdio.h>
40#include <stdlib.h>
41#ifndef WIN32
42#include <unistd.h>
43#endif
44#include <errno.h>
45#include <signal.h>
46#include <string.h>
47#include <time.h>
48
49#include "event-internal.h"
50#include "evmap-internal.h"
51#include "mm-internal.h"
52#include "changelist-internal.h"
53
54/** An entry for an evmap_io list: notes all the events that want to read or
55	write on a given fd, and the number of each.
56  */
57struct evmap_io {
58	struct event_list events;
59	ev_uint16_t nread;
60	ev_uint16_t nwrite;
61};
62
63/* An entry for an evmap_signal list: notes all the events that want to know
64   when a signal triggers. */
65struct evmap_signal {
66	struct event_list events;
67};
68
69/* On some platforms, fds start at 0 and increment by 1 as they are
70   allocated, and old numbers get used.  For these platforms, we
71   implement io maps just like signal maps: as an array of pointers to
72   struct evmap_io.  But on other platforms (windows), sockets are not
73   0-indexed, not necessarily consecutive, and not necessarily reused.
74   There, we use a hashtable to implement evmap_io.
75*/
76#ifdef EVMAP_USE_HT
77struct event_map_entry {
78	HT_ENTRY(event_map_entry) map_node;
79	evutil_socket_t fd;
80	union { /* This is a union in case we need to make more things that can
81			   be in the hashtable. */
82		struct evmap_io evmap_io;
83	} ent;
84};
85
86/* Helper used by the event_io_map hashtable code; tries to return a good hash
87 * of the fd in e->fd. */
88static inline unsigned
89hashsocket(struct event_map_entry *e)
90{
91	/* On win32, in practice, the low 2-3 bits of a SOCKET seem not to
92	 * matter.  Our hashtable implementation really likes low-order bits,
93	 * though, so let's do the rotate-and-add trick. */
94	unsigned h = (unsigned) e->fd;
95	h += (h >> 2) | (h << 30);
96	return h;
97}
98
99/* Helper used by the event_io_map hashtable code; returns true iff e1 and e2
100 * have the same e->fd. */
101static inline int
102eqsocket(struct event_map_entry *e1, struct event_map_entry *e2)
103{
104	return e1->fd == e2->fd;
105}
106
107HT_PROTOTYPE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket)
108HT_GENERATE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket,
109			0.5, mm_malloc, mm_realloc, mm_free)
110
111#define GET_IO_SLOT(x, map, slot, type)					\
112	do {								\
113		struct event_map_entry _key, *_ent;			\
114		_key.fd = slot;						\
115		_ent = HT_FIND(event_io_map, map, &_key);		\
116		(x) = _ent ? &_ent->ent.type : NULL;			\
117	} while (0);
118
119#define GET_IO_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
120	do {								\
121		struct event_map_entry _key, *_ent;			\
122		_key.fd = slot;						\
123		_HT_FIND_OR_INSERT(event_io_map, map_node, hashsocket, map, \
124		    event_map_entry, &_key, ptr,			\
125		    {							\
126			    _ent = *ptr;				\
127		    },							\
128		    {							\
129			    _ent = mm_calloc(1,sizeof(struct event_map_entry)+fdinfo_len); \
130			    if (EVUTIL_UNLIKELY(_ent == NULL))		\
131				    return (-1);			\
132			    _ent->fd = slot;				\
133			    (ctor)(&_ent->ent.type);			\
134			    _HT_FOI_INSERT(map_node, map, &_key, _ent, ptr) \
135				});					\
136		(x) = &_ent->ent.type;					\
137	} while (0)
138
139void evmap_io_initmap(struct event_io_map *ctx)
140{
141	HT_INIT(event_io_map, ctx);
142}
143
144void evmap_io_clear(struct event_io_map *ctx)
145{
146	struct event_map_entry **ent, **next, *this;
147	for (ent = HT_START(event_io_map, ctx); ent; ent = next) {
148		this = *ent;
149		next = HT_NEXT_RMV(event_io_map, ctx, ent);
150		mm_free(this);
151	}
152	HT_CLEAR(event_io_map, ctx); /* remove all storage held by the ctx. */
153}
154#endif
155
156/* Set the variable 'x' to the field in event_map 'map' with fields of type
157   'struct type *' corresponding to the fd or signal 'slot'.  Set 'x' to NULL
158   if there are no entries for 'slot'.  Does no bounds-checking. */
159#define GET_SIGNAL_SLOT(x, map, slot, type)			\
160	(x) = (struct type *)((map)->entries[slot])
161/* As GET_SLOT, but construct the entry for 'slot' if it is not present,
162   by allocating enough memory for a 'struct type', and initializing the new
163   value by calling the function 'ctor' on it.  Makes the function
164   return -1 on allocation failure.
165 */
166#define GET_SIGNAL_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
167	do {								\
168		if ((map)->entries[slot] == NULL) {			\
169			(map)->entries[slot] =				\
170			    mm_calloc(1,sizeof(struct type)+fdinfo_len); \
171			if (EVUTIL_UNLIKELY((map)->entries[slot] == NULL)) \
172				return (-1);				\
173			(ctor)((struct type *)(map)->entries[slot]);	\
174		}							\
175		(x) = (struct type *)((map)->entries[slot]);		\
176	} while (0)
177
178/* If we aren't using hashtables, then define the IO_SLOT macros and functions
179   as thin aliases over the SIGNAL_SLOT versions. */
180#ifndef EVMAP_USE_HT
181#define GET_IO_SLOT(x,map,slot,type) GET_SIGNAL_SLOT(x,map,slot,type)
182#define GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)	\
183	GET_SIGNAL_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)
184#define FDINFO_OFFSET sizeof(struct evmap_io)
185void
186evmap_io_initmap(struct event_io_map* ctx)
187{
188	evmap_signal_initmap(ctx);
189}
190void
191evmap_io_clear(struct event_io_map* ctx)
192{
193	evmap_signal_clear(ctx);
194}
195#endif
196
197
198/** Expand 'map' with new entries of width 'msize' until it is big enough
199	to store a value in 'slot'.
200 */
201static int
202evmap_make_space(struct event_signal_map *map, int slot, int msize)
203{
204	if (map->nentries <= slot) {
205		int nentries = map->nentries ? map->nentries : 32;
206		void **tmp;
207
208		while (nentries <= slot)
209			nentries <<= 1;
210
211		tmp = (void **)mm_realloc(map->entries, nentries * msize);
212		if (tmp == NULL)
213			return (-1);
214
215		memset(&tmp[map->nentries], 0,
216		    (nentries - map->nentries) * msize);
217
218		map->nentries = nentries;
219		map->entries = tmp;
220	}
221
222	return (0);
223}
224
225void
226evmap_signal_initmap(struct event_signal_map *ctx)
227{
228	ctx->nentries = 0;
229	ctx->entries = NULL;
230}
231
232void
233evmap_signal_clear(struct event_signal_map *ctx)
234{
235	if (ctx->entries != NULL) {
236		int i;
237		for (i = 0; i < ctx->nentries; ++i) {
238			if (ctx->entries[i] != NULL)
239				mm_free(ctx->entries[i]);
240		}
241		mm_free(ctx->entries);
242		ctx->entries = NULL;
243	}
244	ctx->nentries = 0;
245}
246
247
248/* code specific to file descriptors */
249
250/** Constructor for struct evmap_io */
251static void
252evmap_io_init(struct evmap_io *entry)
253{
254	TAILQ_INIT(&entry->events);
255	entry->nread = 0;
256	entry->nwrite = 0;
257}
258
259
260/* return -1 on error, 0 on success if nothing changed in the event backend,
261 * and 1 on success if something did. */
262int
263evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
264{
265	const struct eventop *evsel = base->evsel;
266	struct event_io_map *io = &base->io;
267	struct evmap_io *ctx = NULL;
268	int nread, nwrite, retval = 0;
269	short res = 0, old = 0;
270	struct event *old_ev;
271
272	EVUTIL_ASSERT(fd == ev->ev_fd);
273
274	if (fd < 0)
275		return 0;
276
277#ifndef EVMAP_USE_HT
278	if (fd >= io->nentries) {
279		if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
280			return (-1);
281	}
282#endif
283	GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
284						 evsel->fdinfo_len);
285
286	nread = ctx->nread;
287	nwrite = ctx->nwrite;
288
289	if (nread)
290		old |= EV_READ;
291	if (nwrite)
292		old |= EV_WRITE;
293
294	if (ev->ev_events & EV_READ) {
295		if (++nread == 1)
296			res |= EV_READ;
297	}
298	if (ev->ev_events & EV_WRITE) {
299		if (++nwrite == 1)
300			res |= EV_WRITE;
301	}
302	if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff)) {
303		event_warnx("Too many events reading or writing on fd %d",
304		    (int)fd);
305		return -1;
306	}
307	if (EVENT_DEBUG_MODE_IS_ON() &&
308	    (old_ev = TAILQ_FIRST(&ctx->events)) &&
309	    (old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
310		event_warnx("Tried to mix edge-triggered and non-edge-triggered"
311		    " events on fd %d", (int)fd);
312		return -1;
313	}
314
315	if (res) {
316		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
317		/* XXX(niels): we cannot mix edge-triggered and
318		 * level-triggered, we should probably assert on
319		 * this. */
320		if (evsel->add(base, ev->ev_fd,
321			old, (ev->ev_events & EV_ET) | res, extra) == -1)
322			return (-1);
323		retval = 1;
324	}
325
326	ctx->nread = (ev_uint16_t) nread;
327	ctx->nwrite = (ev_uint16_t) nwrite;
328	TAILQ_INSERT_TAIL(&ctx->events, ev, ev_io_next);
329
330	return (retval);
331}
332
333/* return -1 on error, 0 on success if nothing changed in the event backend,
334 * and 1 on success if something did. */
335int
336evmap_io_del(struct event_base *base, evutil_socket_t fd, struct event *ev)
337{
338	const struct eventop *evsel = base->evsel;
339	struct event_io_map *io = &base->io;
340	struct evmap_io *ctx;
341	int nread, nwrite, retval = 0;
342	short res = 0, old = 0;
343
344	if (fd < 0)
345		return 0;
346
347	EVUTIL_ASSERT(fd == ev->ev_fd);
348
349#ifndef EVMAP_USE_HT
350	if (fd >= io->nentries)
351		return (-1);
352#endif
353
354	GET_IO_SLOT(ctx, io, fd, evmap_io);
355
356	nread = ctx->nread;
357	nwrite = ctx->nwrite;
358
359	if (nread)
360		old |= EV_READ;
361	if (nwrite)
362		old |= EV_WRITE;
363
364	if (ev->ev_events & EV_READ) {
365		if (--nread == 0)
366			res |= EV_READ;
367		EVUTIL_ASSERT(nread >= 0);
368	}
369	if (ev->ev_events & EV_WRITE) {
370		if (--nwrite == 0)
371			res |= EV_WRITE;
372		EVUTIL_ASSERT(nwrite >= 0);
373	}
374
375	if (res) {
376		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
377		if (evsel->del(base, ev->ev_fd, old, res, extra) == -1)
378			return (-1);
379		retval = 1;
380	}
381
382	ctx->nread = nread;
383	ctx->nwrite = nwrite;
384	TAILQ_REMOVE(&ctx->events, ev, ev_io_next);
385
386	return (retval);
387}
388
389void
390evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
391{
392	struct event_io_map *io = &base->io;
393	struct evmap_io *ctx;
394	struct event *ev;
395
396#ifndef EVMAP_USE_HT
397	EVUTIL_ASSERT(fd < io->nentries);
398#endif
399	GET_IO_SLOT(ctx, io, fd, evmap_io);
400
401	EVUTIL_ASSERT(ctx);
402	TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
403		if (ev->ev_events & events)
404			event_active_nolock(ev, ev->ev_events & events, 1);
405	}
406}
407
408/* code specific to signals */
409
410static void
411evmap_signal_init(struct evmap_signal *entry)
412{
413	TAILQ_INIT(&entry->events);
414}
415
416
417int
418evmap_signal_add(struct event_base *base, int sig, struct event *ev)
419{
420	const struct eventop *evsel = base->evsigsel;
421	struct event_signal_map *map = &base->sigmap;
422	struct evmap_signal *ctx = NULL;
423
424	if (sig >= map->nentries) {
425		if (evmap_make_space(
426			map, sig, sizeof(struct evmap_signal *)) == -1)
427			return (-1);
428	}
429	GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
430	    base->evsigsel->fdinfo_len);
431
432	if (TAILQ_EMPTY(&ctx->events)) {
433		if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
434		    == -1)
435			return (-1);
436	}
437
438	TAILQ_INSERT_TAIL(&ctx->events, ev, ev_signal_next);
439
440	return (1);
441}
442
443int
444evmap_signal_del(struct event_base *base, int sig, struct event *ev)
445{
446	const struct eventop *evsel = base->evsigsel;
447	struct event_signal_map *map = &base->sigmap;
448	struct evmap_signal *ctx;
449
450	if (sig >= map->nentries)
451		return (-1);
452
453	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
454
455	if (TAILQ_FIRST(&ctx->events) == TAILQ_LAST(&ctx->events, event_list)) {
456		if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
457			return (-1);
458	}
459
460	TAILQ_REMOVE(&ctx->events, ev, ev_signal_next);
461
462	return (1);
463}
464
465void
466evmap_signal_active(struct event_base *base, evutil_socket_t sig, int ncalls)
467{
468	struct event_signal_map *map = &base->sigmap;
469	struct evmap_signal *ctx;
470	struct event *ev;
471
472	EVUTIL_ASSERT(sig < map->nentries);
473	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
474
475	TAILQ_FOREACH(ev, &ctx->events, ev_signal_next)
476		event_active_nolock(ev, EV_SIGNAL, ncalls);
477}
478
479void *
480evmap_io_get_fdinfo(struct event_io_map *map, evutil_socket_t fd)
481{
482	struct evmap_io *ctx;
483	GET_IO_SLOT(ctx, map, fd, evmap_io);
484	if (ctx)
485		return ((char*)ctx) + sizeof(struct evmap_io);
486	else
487		return NULL;
488}
489
490/** Per-fd structure for use with changelists.  It keeps track, for each fd or
491 * signal using the changelist, of where its entry in the changelist is.
492 */
493struct event_changelist_fdinfo {
494	int idxplus1; /* this is the index +1, so that memset(0) will make it
495		       * a no-such-element */
496};
497
498void
499event_changelist_init(struct event_changelist *changelist)
500{
501	changelist->changes = NULL;
502	changelist->changes_size = 0;
503	changelist->n_changes = 0;
504}
505
506/** Helper: return the changelist_fdinfo corresponding to a given change. */
507static inline struct event_changelist_fdinfo *
508event_change_get_fdinfo(struct event_base *base,
509    const struct event_change *change)
510{
511	char *ptr;
512	if (change->read_change & EV_CHANGE_SIGNAL) {
513		struct evmap_signal *ctx;
514		GET_SIGNAL_SLOT(ctx, &base->sigmap, change->fd, evmap_signal);
515		ptr = ((char*)ctx) + sizeof(struct evmap_signal);
516	} else {
517		struct evmap_io *ctx;
518		GET_IO_SLOT(ctx, &base->io, change->fd, evmap_io);
519		ptr = ((char*)ctx) + sizeof(struct evmap_io);
520	}
521	return (void*)ptr;
522}
523
524#ifdef DEBUG_CHANGELIST
525/** Make sure that the changelist is consistent with the evmap structures. */
526static void
527event_changelist_check(struct event_base *base)
528{
529	int i;
530	struct event_changelist *changelist = &base->changelist;
531
532	EVUTIL_ASSERT(changelist->changes_size >= changelist->n_changes);
533	for (i = 0; i < changelist->n_changes; ++i) {
534		struct event_change *c = &changelist->changes[i];
535		struct event_changelist_fdinfo *f;
536		EVUTIL_ASSERT(c->fd >= 0);
537		f = event_change_get_fdinfo(base, c);
538		EVUTIL_ASSERT(f);
539		EVUTIL_ASSERT(f->idxplus1 == i + 1);
540	}
541
542	for (i = 0; i < base->io.nentries; ++i) {
543		struct evmap_io *io = base->io.entries[i];
544		struct event_changelist_fdinfo *f;
545		if (!io)
546			continue;
547		f = (void*)
548		    ( ((char*)io) + sizeof(struct evmap_io) );
549		if (f->idxplus1) {
550			struct event_change *c = &changelist->changes[f->idxplus1 - 1];
551			EVUTIL_ASSERT(c->fd == i);
552		}
553	}
554}
555#else
556#define event_changelist_check(base)  ((void)0)
557#endif
558
559void
560event_changelist_remove_all(struct event_changelist *changelist,
561    struct event_base *base)
562{
563	int i;
564
565	event_changelist_check(base);
566
567	for (i = 0; i < changelist->n_changes; ++i) {
568		struct event_change *ch = &changelist->changes[i];
569		struct event_changelist_fdinfo *fdinfo =
570		    event_change_get_fdinfo(base, ch);
571		EVUTIL_ASSERT(fdinfo->idxplus1 == i + 1);
572		fdinfo->idxplus1 = 0;
573	}
574
575	changelist->n_changes = 0;
576
577	event_changelist_check(base);
578}
579
580void
581event_changelist_freemem(struct event_changelist *changelist)
582{
583	if (changelist->changes)
584		mm_free(changelist->changes);
585	event_changelist_init(changelist); /* zero it all out. */
586}
587
588/** Increase the size of 'changelist' to hold more changes. */
589static int
590event_changelist_grow(struct event_changelist *changelist)
591{
592	int new_size;
593	struct event_change *new_changes;
594	if (changelist->changes_size < 64)
595		new_size = 64;
596	else
597		new_size = changelist->changes_size * 2;
598
599	new_changes = mm_realloc(changelist->changes,
600	    new_size * sizeof(struct event_change));
601
602	if (EVUTIL_UNLIKELY(new_changes == NULL))
603		return (-1);
604
605	changelist->changes = new_changes;
606	changelist->changes_size = new_size;
607
608	return (0);
609}
610
611/** Return a pointer to the changelist entry for the file descriptor or signal
612 * 'fd', whose fdinfo is 'fdinfo'.  If none exists, construct it, setting its
613 * old_events field to old_events.
614 */
615static struct event_change *
616event_changelist_get_or_construct(struct event_changelist *changelist,
617    evutil_socket_t fd,
618    short old_events,
619    struct event_changelist_fdinfo *fdinfo)
620{
621	struct event_change *change;
622
623	if (fdinfo->idxplus1 == 0) {
624		int idx;
625		EVUTIL_ASSERT(changelist->n_changes <= changelist->changes_size);
626
627		if (changelist->n_changes == changelist->changes_size) {
628			if (event_changelist_grow(changelist) < 0)
629				return NULL;
630		}
631
632		idx = changelist->n_changes++;
633		change = &changelist->changes[idx];
634		fdinfo->idxplus1 = idx + 1;
635
636		memset(change, 0, sizeof(struct event_change));
637		change->fd = fd;
638		change->old_events = old_events;
639	} else {
640		change = &changelist->changes[fdinfo->idxplus1 - 1];
641		EVUTIL_ASSERT(change->fd == fd);
642	}
643	return change;
644}
645
646int
647event_changelist_add(struct event_base *base, evutil_socket_t fd, short old, short events,
648    void *p)
649{
650	struct event_changelist *changelist = &base->changelist;
651	struct event_changelist_fdinfo *fdinfo = p;
652	struct event_change *change;
653
654	event_changelist_check(base);
655
656	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
657	if (!change)
658		return -1;
659
660	/* An add replaces any previous delete, but doesn't result in a no-op,
661	 * since the delete might fail (because the fd had been closed since
662	 * the last add, for instance. */
663
664	if (events & (EV_READ|EV_SIGNAL)) {
665		change->read_change = EV_CHANGE_ADD |
666		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
667	}
668	if (events & EV_WRITE) {
669		change->write_change = EV_CHANGE_ADD |
670		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
671	}
672
673	event_changelist_check(base);
674	return (0);
675}
676
677int
678event_changelist_del(struct event_base *base, evutil_socket_t fd, short old, short events,
679    void *p)
680{
681	struct event_changelist *changelist = &base->changelist;
682	struct event_changelist_fdinfo *fdinfo = p;
683	struct event_change *change;
684
685	event_changelist_check(base);
686	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
687	event_changelist_check(base);
688	if (!change)
689		return -1;
690
691	/* A delete removes any previous add, rather than replacing it:
692	   on those platforms where "add, delete, dispatch" is not the same
693	   as "no-op, dispatch", we want the no-op behavior.
694
695	   As well as checking the current operation we should also check
696	   the original set of events to make sure were not ignoring
697	   the case where the add operation is present on an event that
698	   was already set.
699
700	   If we have a no-op item, we could remove it it from the list
701	   entirely, but really there's not much point: skipping the no-op
702	   change when we do the dispatch later is far cheaper than rejuggling
703	   the array now.
704
705	   As this stands, it also lets through deletions of events that are
706	   not currently set.
707	 */
708
709	if (events & (EV_READ|EV_SIGNAL)) {
710		if (!(change->old_events & (EV_READ | EV_SIGNAL)) &&
711		    (change->read_change & EV_CHANGE_ADD))
712			change->read_change = 0;
713		else
714			change->read_change = EV_CHANGE_DEL;
715	}
716	if (events & EV_WRITE) {
717		if (!(change->old_events & EV_WRITE) &&
718		    (change->write_change & EV_CHANGE_ADD))
719			change->write_change = 0;
720		else
721			change->write_change = EV_CHANGE_DEL;
722	}
723
724	event_changelist_check(base);
725	return (0);
726}
727
728void
729evmap_check_integrity(struct event_base *base)
730{
731#define EVLIST_X_SIGFOUND 0x1000
732#define EVLIST_X_IOFOUND 0x2000
733
734	evutil_socket_t i;
735	struct event *ev;
736	struct event_io_map *io = &base->io;
737	struct event_signal_map *sigmap = &base->sigmap;
738#ifdef EVMAP_USE_HT
739	struct event_map_entry **mapent;
740#endif
741	int nsignals, ntimers, nio;
742	nsignals = ntimers = nio = 0;
743
744	TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
745		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
746		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INIT);
747		ev->ev_flags &= ~(EVLIST_X_SIGFOUND|EVLIST_X_IOFOUND);
748	}
749
750#ifdef EVMAP_USE_HT
751	HT_FOREACH(mapent, event_io_map, io) {
752		struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
753		i = (*mapent)->fd;
754#else
755	for (i = 0; i < io->nentries; ++i) {
756		struct evmap_io *ctx = io->entries[i];
757
758		if (!ctx)
759			continue;
760#endif
761
762		TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
763			EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_IOFOUND));
764			EVUTIL_ASSERT(ev->ev_fd == i);
765			ev->ev_flags |= EVLIST_X_IOFOUND;
766			nio++;
767		}
768	}
769
770	for (i = 0; i < sigmap->nentries; ++i) {
771		struct evmap_signal *ctx = sigmap->entries[i];
772		if (!ctx)
773			continue;
774
775		TAILQ_FOREACH(ev, &ctx->events, ev_signal_next) {
776			EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_SIGFOUND));
777			EVUTIL_ASSERT(ev->ev_fd == i);
778			ev->ev_flags |= EVLIST_X_SIGFOUND;
779			nsignals++;
780		}
781	}
782
783	TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
784		if (ev->ev_events & (EV_READ|EV_WRITE)) {
785			EVUTIL_ASSERT(ev->ev_flags & EVLIST_X_IOFOUND);
786			--nio;
787		}
788		if (ev->ev_events & EV_SIGNAL) {
789			EVUTIL_ASSERT(ev->ev_flags & EVLIST_X_SIGFOUND);
790			--nsignals;
791		}
792	}
793
794	EVUTIL_ASSERT(nio == 0);
795	EVUTIL_ASSERT(nsignals == 0);
796	/* There is no "EVUTIL_ASSERT(ntimers == 0)": eventqueue is only for
797	 * pending signals and io events.
798	 */
799}
800