xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/evmap.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: evmap.c,v 1.6 2024/08/18 20:47:21 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*
48585484eSchristos  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
58585484eSchristos  *
68585484eSchristos  * Redistribution and use in source and binary forms, with or without
78585484eSchristos  * modification, are permitted provided that the following conditions
88585484eSchristos  * are met:
98585484eSchristos  * 1. Redistributions of source code must retain the above copyright
108585484eSchristos  *    notice, this list of conditions and the following disclaimer.
118585484eSchristos  * 2. Redistributions in binary form must reproduce the above copyright
128585484eSchristos  *    notice, this list of conditions and the following disclaimer in the
138585484eSchristos  *    documentation and/or other materials provided with the distribution.
148585484eSchristos  * 3. The name of the author may not be used to endorse or promote products
158585484eSchristos  *    derived from this software without specific prior written permission.
168585484eSchristos  *
178585484eSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
188585484eSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
198585484eSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
208585484eSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
218585484eSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
228585484eSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
238585484eSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
248585484eSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
258585484eSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
268585484eSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
278585484eSchristos  */
288585484eSchristos #include "event2/event-config.h"
298585484eSchristos #include "evconfig-private.h"
308585484eSchristos 
318585484eSchristos #ifdef _WIN32
328585484eSchristos #include <winsock2.h>
338585484eSchristos #define WIN32_LEAN_AND_MEAN
348585484eSchristos #include <windows.h>
358585484eSchristos #undef WIN32_LEAN_AND_MEAN
368585484eSchristos #endif
378585484eSchristos #include <sys/types.h>
388585484eSchristos #if !defined(_WIN32) && defined(EVENT__HAVE_SYS_TIME_H)
398585484eSchristos #include <sys/time.h>
408585484eSchristos #endif
418585484eSchristos #include <sys/queue.h>
428585484eSchristos #include <stdio.h>
438585484eSchristos #include <stdlib.h>
448585484eSchristos #ifndef _WIN32
458585484eSchristos #include <unistd.h>
468585484eSchristos #endif
478585484eSchristos #include <errno.h>
48*eabc0478Schristos #include <limits.h>
498585484eSchristos #include <signal.h>
508585484eSchristos #include <string.h>
518585484eSchristos #include <time.h>
528585484eSchristos 
538585484eSchristos #include "event-internal.h"
548585484eSchristos #include "evmap-internal.h"
558585484eSchristos #include "mm-internal.h"
568585484eSchristos #include "changelist-internal.h"
578585484eSchristos 
588585484eSchristos /** An entry for an evmap_io list: notes all the events that want to read or
598585484eSchristos 	write on a given fd, and the number of each.
608585484eSchristos   */
618585484eSchristos struct evmap_io {
628585484eSchristos 	struct event_dlist events;
638585484eSchristos 	ev_uint16_t nread;
648585484eSchristos 	ev_uint16_t nwrite;
65b8ecfcfeSchristos 	ev_uint16_t nclose;
668585484eSchristos };
678585484eSchristos 
688585484eSchristos /* An entry for an evmap_signal list: notes all the events that want to know
698585484eSchristos    when a signal triggers. */
708585484eSchristos struct evmap_signal {
718585484eSchristos 	struct event_dlist events;
728585484eSchristos };
738585484eSchristos 
748585484eSchristos /* On some platforms, fds start at 0 and increment by 1 as they are
758585484eSchristos    allocated, and old numbers get used.  For these platforms, we
768585484eSchristos    implement io maps just like signal maps: as an array of pointers to
778585484eSchristos    struct evmap_io.  But on other platforms (windows), sockets are not
788585484eSchristos    0-indexed, not necessarily consecutive, and not necessarily reused.
798585484eSchristos    There, we use a hashtable to implement evmap_io.
808585484eSchristos */
818585484eSchristos #ifdef EVMAP_USE_HT
828585484eSchristos struct event_map_entry {
838585484eSchristos 	HT_ENTRY(event_map_entry) map_node;
848585484eSchristos 	evutil_socket_t fd;
858585484eSchristos 	union { /* This is a union in case we need to make more things that can
868585484eSchristos 			   be in the hashtable. */
878585484eSchristos 		struct evmap_io evmap_io;
888585484eSchristos 	} ent;
898585484eSchristos };
908585484eSchristos 
918585484eSchristos /* Helper used by the event_io_map hashtable code; tries to return a good hash
928585484eSchristos  * of the fd in e->fd. */
938585484eSchristos static inline unsigned
948585484eSchristos hashsocket(struct event_map_entry *e)
958585484eSchristos {
968585484eSchristos 	/* On win32, in practice, the low 2-3 bits of a SOCKET seem not to
978585484eSchristos 	 * matter.  Our hashtable implementation really likes low-order bits,
988585484eSchristos 	 * though, so let's do the rotate-and-add trick. */
998585484eSchristos 	unsigned h = (unsigned) e->fd;
1008585484eSchristos 	h += (h >> 2) | (h << 30);
1018585484eSchristos 	return h;
1028585484eSchristos }
1038585484eSchristos 
1048585484eSchristos /* Helper used by the event_io_map hashtable code; returns true iff e1 and e2
1058585484eSchristos  * have the same e->fd. */
1068585484eSchristos static inline int
1078585484eSchristos eqsocket(struct event_map_entry *e1, struct event_map_entry *e2)
1088585484eSchristos {
1098585484eSchristos 	return e1->fd == e2->fd;
1108585484eSchristos }
1118585484eSchristos 
1128585484eSchristos HT_PROTOTYPE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket)
1138585484eSchristos HT_GENERATE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket,
1148585484eSchristos 			0.5, mm_malloc, mm_realloc, mm_free)
1158585484eSchristos 
1168585484eSchristos #define GET_IO_SLOT(x, map, slot, type)					\
1178585484eSchristos 	do {								\
1188585484eSchristos 		struct event_map_entry key_, *ent_;			\
1198585484eSchristos 		key_.fd = slot;						\
1208585484eSchristos 		ent_ = HT_FIND(event_io_map, map, &key_);		\
1218585484eSchristos 		(x) = ent_ ? &ent_->ent.type : NULL;			\
1228585484eSchristos 	} while (0);
1238585484eSchristos 
1248585484eSchristos #define GET_IO_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
1258585484eSchristos 	do {								\
1268585484eSchristos 		struct event_map_entry key_, *ent_;			\
1278585484eSchristos 		key_.fd = slot;						\
1288585484eSchristos 		HT_FIND_OR_INSERT_(event_io_map, map_node, hashsocket, map, \
1298585484eSchristos 		    event_map_entry, &key_, ptr,			\
1308585484eSchristos 		    {							\
1318585484eSchristos 			    ent_ = *ptr;				\
1328585484eSchristos 		    },							\
1338585484eSchristos 		    {							\
1348585484eSchristos 			    ent_ = mm_calloc(1,sizeof(struct event_map_entry)+fdinfo_len); \
1358585484eSchristos 			    if (EVUTIL_UNLIKELY(ent_ == NULL))		\
1368585484eSchristos 				    return (-1);			\
1378585484eSchristos 			    ent_->fd = slot;				\
1388585484eSchristos 			    (ctor)(&ent_->ent.type);			\
1398585484eSchristos 			    HT_FOI_INSERT_(map_node, map, &key_, ent_, ptr) \
1408585484eSchristos 				});					\
1418585484eSchristos 		(x) = &ent_->ent.type;					\
1428585484eSchristos 	} while (0)
1438585484eSchristos 
1448585484eSchristos void evmap_io_initmap_(struct event_io_map *ctx)
1458585484eSchristos {
1468585484eSchristos 	HT_INIT(event_io_map, ctx);
1478585484eSchristos }
1488585484eSchristos 
1498585484eSchristos void evmap_io_clear_(struct event_io_map *ctx)
1508585484eSchristos {
1518585484eSchristos 	struct event_map_entry **ent, **next, *this;
1528585484eSchristos 	for (ent = HT_START(event_io_map, ctx); ent; ent = next) {
1538585484eSchristos 		this = *ent;
1548585484eSchristos 		next = HT_NEXT_RMV(event_io_map, ctx, ent);
1558585484eSchristos 		mm_free(this);
1568585484eSchristos 	}
1578585484eSchristos 	HT_CLEAR(event_io_map, ctx); /* remove all storage held by the ctx. */
1588585484eSchristos }
1598585484eSchristos #endif
1608585484eSchristos 
1618585484eSchristos /* Set the variable 'x' to the field in event_map 'map' with fields of type
1628585484eSchristos    'struct type *' corresponding to the fd or signal 'slot'.  Set 'x' to NULL
1638585484eSchristos    if there are no entries for 'slot'.  Does no bounds-checking. */
1648585484eSchristos #define GET_SIGNAL_SLOT(x, map, slot, type)			\
1658585484eSchristos 	(x) = (struct type *)((map)->entries[slot])
1668585484eSchristos /* As GET_SLOT, but construct the entry for 'slot' if it is not present,
1678585484eSchristos    by allocating enough memory for a 'struct type', and initializing the new
1688585484eSchristos    value by calling the function 'ctor' on it.  Makes the function
1698585484eSchristos    return -1 on allocation failure.
1708585484eSchristos  */
1718585484eSchristos #define GET_SIGNAL_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
1728585484eSchristos 	do {								\
1738585484eSchristos 		if ((map)->entries[slot] == NULL) {			\
1748585484eSchristos 			(map)->entries[slot] =				\
1758585484eSchristos 			    mm_calloc(1,sizeof(struct type)+fdinfo_len); \
1768585484eSchristos 			if (EVUTIL_UNLIKELY((map)->entries[slot] == NULL)) \
1778585484eSchristos 				return (-1);				\
1788585484eSchristos 			(ctor)((struct type *)(map)->entries[slot]);	\
1798585484eSchristos 		}							\
1808585484eSchristos 		(x) = (struct type *)((map)->entries[slot]);		\
1818585484eSchristos 	} while (0)
1828585484eSchristos 
1838585484eSchristos /* If we aren't using hashtables, then define the IO_SLOT macros and functions
1848585484eSchristos    as thin aliases over the SIGNAL_SLOT versions. */
1858585484eSchristos #ifndef EVMAP_USE_HT
1868585484eSchristos #define GET_IO_SLOT(x,map,slot,type) GET_SIGNAL_SLOT(x,map,slot,type)
1878585484eSchristos #define GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)	\
1888585484eSchristos 	GET_SIGNAL_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)
1898585484eSchristos #define FDINFO_OFFSET sizeof(struct evmap_io)
1908585484eSchristos void
1918585484eSchristos evmap_io_initmap_(struct event_io_map* ctx)
1928585484eSchristos {
1938585484eSchristos 	evmap_signal_initmap_(ctx);
1948585484eSchristos }
1958585484eSchristos void
1968585484eSchristos evmap_io_clear_(struct event_io_map* ctx)
1978585484eSchristos {
1988585484eSchristos 	evmap_signal_clear_(ctx);
1998585484eSchristos }
2008585484eSchristos #endif
2018585484eSchristos 
2028585484eSchristos 
2038585484eSchristos /** Expand 'map' with new entries of width 'msize' until it is big enough
2048585484eSchristos 	to store a value in 'slot'.
2058585484eSchristos  */
2068585484eSchristos static int
2078585484eSchristos evmap_make_space(struct event_signal_map *map, int slot, int msize)
2088585484eSchristos {
2098585484eSchristos 	if (map->nentries <= slot) {
2108585484eSchristos 		int nentries = map->nentries ? map->nentries : 32;
2118585484eSchristos 		void **tmp;
2128585484eSchristos 
213*eabc0478Schristos 		if (slot > INT_MAX / 2)
214*eabc0478Schristos 			return (-1);
215*eabc0478Schristos 
2168585484eSchristos 		while (nentries <= slot)
2178585484eSchristos 			nentries <<= 1;
2188585484eSchristos 
219*eabc0478Schristos 		if (nentries > INT_MAX / msize)
220*eabc0478Schristos 			return (-1);
221*eabc0478Schristos 
2228585484eSchristos 		tmp = (void **)mm_realloc(map->entries, nentries * msize);
2238585484eSchristos 		if (tmp == NULL)
2248585484eSchristos 			return (-1);
2258585484eSchristos 
2268585484eSchristos 		memset(&tmp[map->nentries], 0,
2278585484eSchristos 		    (nentries - map->nentries) * msize);
2288585484eSchristos 
2298585484eSchristos 		map->nentries = nentries;
2308585484eSchristos 		map->entries = tmp;
2318585484eSchristos 	}
2328585484eSchristos 
2338585484eSchristos 	return (0);
2348585484eSchristos }
2358585484eSchristos 
2368585484eSchristos void
2378585484eSchristos evmap_signal_initmap_(struct event_signal_map *ctx)
2388585484eSchristos {
2398585484eSchristos 	ctx->nentries = 0;
2408585484eSchristos 	ctx->entries = NULL;
2418585484eSchristos }
2428585484eSchristos 
2438585484eSchristos void
2448585484eSchristos evmap_signal_clear_(struct event_signal_map *ctx)
2458585484eSchristos {
2468585484eSchristos 	if (ctx->entries != NULL) {
2478585484eSchristos 		int i;
2488585484eSchristos 		for (i = 0; i < ctx->nentries; ++i) {
2498585484eSchristos 			if (ctx->entries[i] != NULL)
2508585484eSchristos 				mm_free(ctx->entries[i]);
2518585484eSchristos 		}
2528585484eSchristos 		mm_free(ctx->entries);
2538585484eSchristos 		ctx->entries = NULL;
2548585484eSchristos 	}
2558585484eSchristos 	ctx->nentries = 0;
2568585484eSchristos }
2578585484eSchristos 
2588585484eSchristos 
2598585484eSchristos /* code specific to file descriptors */
2608585484eSchristos 
2618585484eSchristos /** Constructor for struct evmap_io */
2628585484eSchristos static void
2638585484eSchristos evmap_io_init(struct evmap_io *entry)
2648585484eSchristos {
2658585484eSchristos 	LIST_INIT(&entry->events);
2668585484eSchristos 	entry->nread = 0;
2678585484eSchristos 	entry->nwrite = 0;
268b8ecfcfeSchristos 	entry->nclose = 0;
2698585484eSchristos }
2708585484eSchristos 
2718585484eSchristos 
2728585484eSchristos /* return -1 on error, 0 on success if nothing changed in the event backend,
2738585484eSchristos  * and 1 on success if something did. */
2748585484eSchristos int
2758585484eSchristos evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
2768585484eSchristos {
2778585484eSchristos 	const struct eventop *evsel = base->evsel;
2788585484eSchristos 	struct event_io_map *io = &base->io;
2798585484eSchristos 	struct evmap_io *ctx = NULL;
280b8ecfcfeSchristos 	int nread, nwrite, nclose, retval = 0;
2818585484eSchristos 	short res = 0, old = 0;
2828585484eSchristos 	struct event *old_ev;
2838585484eSchristos 
2848585484eSchristos 	EVUTIL_ASSERT(fd == ev->ev_fd);
2858585484eSchristos 
2868585484eSchristos 	if (fd < 0)
2878585484eSchristos 		return 0;
2888585484eSchristos 
2898585484eSchristos #ifndef EVMAP_USE_HT
2908585484eSchristos 	if (fd >= io->nentries) {
2918585484eSchristos 		if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
2928585484eSchristos 			return (-1);
2938585484eSchristos 	}
2948585484eSchristos #endif
2958585484eSchristos 	GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
2968585484eSchristos 						 evsel->fdinfo_len);
2978585484eSchristos 
2988585484eSchristos 	nread = ctx->nread;
2998585484eSchristos 	nwrite = ctx->nwrite;
300b8ecfcfeSchristos 	nclose = ctx->nclose;
3018585484eSchristos 
3028585484eSchristos 	if (nread)
3038585484eSchristos 		old |= EV_READ;
3048585484eSchristos 	if (nwrite)
3058585484eSchristos 		old |= EV_WRITE;
306b8ecfcfeSchristos 	if (nclose)
307b8ecfcfeSchristos 		old |= EV_CLOSED;
3088585484eSchristos 
3098585484eSchristos 	if (ev->ev_events & EV_READ) {
3108585484eSchristos 		if (++nread == 1)
3118585484eSchristos 			res |= EV_READ;
3128585484eSchristos 	}
3138585484eSchristos 	if (ev->ev_events & EV_WRITE) {
3148585484eSchristos 		if (++nwrite == 1)
3158585484eSchristos 			res |= EV_WRITE;
3168585484eSchristos 	}
317b8ecfcfeSchristos 	if (ev->ev_events & EV_CLOSED) {
318b8ecfcfeSchristos 		if (++nclose == 1)
319b8ecfcfeSchristos 			res |= EV_CLOSED;
320b8ecfcfeSchristos 	}
321b8ecfcfeSchristos 	if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
3228585484eSchristos 		event_warnx("Too many events reading or writing on fd %d",
3238585484eSchristos 		    (int)fd);
3248585484eSchristos 		return -1;
3258585484eSchristos 	}
3268585484eSchristos 	if (EVENT_DEBUG_MODE_IS_ON() &&
3278585484eSchristos 	    (old_ev = LIST_FIRST(&ctx->events)) &&
3288585484eSchristos 	    (old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
3298585484eSchristos 		event_warnx("Tried to mix edge-triggered and non-edge-triggered"
3308585484eSchristos 		    " events on fd %d", (int)fd);
3318585484eSchristos 		return -1;
3328585484eSchristos 	}
3338585484eSchristos 
3348585484eSchristos 	if (res) {
3358585484eSchristos 		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
3368585484eSchristos 		/* XXX(niels): we cannot mix edge-triggered and
3378585484eSchristos 		 * level-triggered, we should probably assert on
3388585484eSchristos 		 * this. */
3398585484eSchristos 		if (evsel->add(base, ev->ev_fd,
3408585484eSchristos 			old, (ev->ev_events & EV_ET) | res, extra) == -1)
3418585484eSchristos 			return (-1);
3428585484eSchristos 		retval = 1;
3438585484eSchristos 	}
3448585484eSchristos 
3458585484eSchristos 	ctx->nread = (ev_uint16_t) nread;
3468585484eSchristos 	ctx->nwrite = (ev_uint16_t) nwrite;
347b8ecfcfeSchristos 	ctx->nclose = (ev_uint16_t) nclose;
3488585484eSchristos 	LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
3498585484eSchristos 
3508585484eSchristos 	return (retval);
3518585484eSchristos }
3528585484eSchristos 
3538585484eSchristos /* return -1 on error, 0 on success if nothing changed in the event backend,
3548585484eSchristos  * and 1 on success if something did. */
3558585484eSchristos int
3568585484eSchristos evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev)
3578585484eSchristos {
3588585484eSchristos 	const struct eventop *evsel = base->evsel;
3598585484eSchristos 	struct event_io_map *io = &base->io;
3608585484eSchristos 	struct evmap_io *ctx;
361b8ecfcfeSchristos 	int nread, nwrite, nclose, retval = 0;
3628585484eSchristos 	short res = 0, old = 0;
3638585484eSchristos 
3648585484eSchristos 	if (fd < 0)
3658585484eSchristos 		return 0;
3668585484eSchristos 
3678585484eSchristos 	EVUTIL_ASSERT(fd == ev->ev_fd);
3688585484eSchristos 
3698585484eSchristos #ifndef EVMAP_USE_HT
3708585484eSchristos 	if (fd >= io->nentries)
3718585484eSchristos 		return (-1);
3728585484eSchristos #endif
3738585484eSchristos 
3748585484eSchristos 	GET_IO_SLOT(ctx, io, fd, evmap_io);
3758585484eSchristos 
3768585484eSchristos 	nread = ctx->nread;
3778585484eSchristos 	nwrite = ctx->nwrite;
378b8ecfcfeSchristos 	nclose = ctx->nclose;
3798585484eSchristos 
3808585484eSchristos 	if (nread)
3818585484eSchristos 		old |= EV_READ;
3828585484eSchristos 	if (nwrite)
3838585484eSchristos 		old |= EV_WRITE;
384b8ecfcfeSchristos 	if (nclose)
385b8ecfcfeSchristos 		old |= EV_CLOSED;
3868585484eSchristos 
3878585484eSchristos 	if (ev->ev_events & EV_READ) {
3888585484eSchristos 		if (--nread == 0)
3898585484eSchristos 			res |= EV_READ;
3908585484eSchristos 		EVUTIL_ASSERT(nread >= 0);
3918585484eSchristos 	}
3928585484eSchristos 	if (ev->ev_events & EV_WRITE) {
3938585484eSchristos 		if (--nwrite == 0)
3948585484eSchristos 			res |= EV_WRITE;
3958585484eSchristos 		EVUTIL_ASSERT(nwrite >= 0);
3968585484eSchristos 	}
397b8ecfcfeSchristos 	if (ev->ev_events & EV_CLOSED) {
398b8ecfcfeSchristos 		if (--nclose == 0)
399b8ecfcfeSchristos 			res |= EV_CLOSED;
400b8ecfcfeSchristos 		EVUTIL_ASSERT(nclose >= 0);
401b8ecfcfeSchristos 	}
4028585484eSchristos 
4038585484eSchristos 	if (res) {
4048585484eSchristos 		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
405*eabc0478Schristos 		if (evsel->del(base, ev->ev_fd,
406*eabc0478Schristos 			old, (ev->ev_events & EV_ET) | res, extra) == -1) {
407b8ecfcfeSchristos 			retval = -1;
408b8ecfcfeSchristos 		} else {
4098585484eSchristos 			retval = 1;
4108585484eSchristos 		}
411b8ecfcfeSchristos 	}
4128585484eSchristos 
4138585484eSchristos 	ctx->nread = nread;
4148585484eSchristos 	ctx->nwrite = nwrite;
415b8ecfcfeSchristos 	ctx->nclose = nclose;
4168585484eSchristos 	LIST_REMOVE(ev, ev_io_next);
4178585484eSchristos 
4188585484eSchristos 	return (retval);
4198585484eSchristos }
4208585484eSchristos 
4218585484eSchristos void
4228585484eSchristos evmap_io_active_(struct event_base *base, evutil_socket_t fd, short events)
4238585484eSchristos {
4248585484eSchristos 	struct event_io_map *io = &base->io;
4258585484eSchristos 	struct evmap_io *ctx;
4268585484eSchristos 	struct event *ev;
4278585484eSchristos 
4288585484eSchristos #ifndef EVMAP_USE_HT
429b8ecfcfeSchristos 	if (fd < 0 || fd >= io->nentries)
430b8ecfcfeSchristos 		return;
4318585484eSchristos #endif
4328585484eSchristos 	GET_IO_SLOT(ctx, io, fd, evmap_io);
4338585484eSchristos 
434b8ecfcfeSchristos 	if (NULL == ctx)
435b8ecfcfeSchristos 		return;
4368585484eSchristos 	LIST_FOREACH(ev, &ctx->events, ev_io_next) {
437*eabc0478Schristos 		if (ev->ev_events & (events & ~EV_ET))
4388585484eSchristos 			event_active_nolock_(ev, ev->ev_events & events, 1);
4398585484eSchristos 	}
4408585484eSchristos }
4418585484eSchristos 
4428585484eSchristos /* code specific to signals */
4438585484eSchristos 
4448585484eSchristos static void
4458585484eSchristos evmap_signal_init(struct evmap_signal *entry)
4468585484eSchristos {
4478585484eSchristos 	LIST_INIT(&entry->events);
4488585484eSchristos }
4498585484eSchristos 
4508585484eSchristos 
4518585484eSchristos int
4528585484eSchristos evmap_signal_add_(struct event_base *base, int sig, struct event *ev)
4538585484eSchristos {
4548585484eSchristos 	const struct eventop *evsel = base->evsigsel;
4558585484eSchristos 	struct event_signal_map *map = &base->sigmap;
4568585484eSchristos 	struct evmap_signal *ctx = NULL;
4578585484eSchristos 
458*eabc0478Schristos 	if (sig < 0 || sig >= NSIG)
459*eabc0478Schristos 		return (-1);
460*eabc0478Schristos 
4618585484eSchristos 	if (sig >= map->nentries) {
4628585484eSchristos 		if (evmap_make_space(
4638585484eSchristos 			map, sig, sizeof(struct evmap_signal *)) == -1)
4648585484eSchristos 			return (-1);
4658585484eSchristos 	}
4668585484eSchristos 	GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
4678585484eSchristos 	    base->evsigsel->fdinfo_len);
4688585484eSchristos 
4698585484eSchristos 	if (LIST_EMPTY(&ctx->events)) {
4708585484eSchristos 		if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
4718585484eSchristos 		    == -1)
4728585484eSchristos 			return (-1);
4738585484eSchristos 	}
4748585484eSchristos 
4758585484eSchristos 	LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);
4768585484eSchristos 
4778585484eSchristos 	return (1);
4788585484eSchristos }
4798585484eSchristos 
4808585484eSchristos int
4818585484eSchristos evmap_signal_del_(struct event_base *base, int sig, struct event *ev)
4828585484eSchristos {
4838585484eSchristos 	const struct eventop *evsel = base->evsigsel;
4848585484eSchristos 	struct event_signal_map *map = &base->sigmap;
4858585484eSchristos 	struct evmap_signal *ctx;
4868585484eSchristos 
487*eabc0478Schristos 	if (sig < 0 || sig >= map->nentries)
4888585484eSchristos 		return (-1);
4898585484eSchristos 
4908585484eSchristos 	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
4918585484eSchristos 
4928585484eSchristos 	LIST_REMOVE(ev, ev_signal_next);
4938585484eSchristos 
4948585484eSchristos 	if (LIST_FIRST(&ctx->events) == NULL) {
4958585484eSchristos 		if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
4968585484eSchristos 			return (-1);
4978585484eSchristos 	}
4988585484eSchristos 
4998585484eSchristos 	return (1);
5008585484eSchristos }
5018585484eSchristos 
5028585484eSchristos void
5038585484eSchristos evmap_signal_active_(struct event_base *base, evutil_socket_t sig, int ncalls)
5048585484eSchristos {
5058585484eSchristos 	struct event_signal_map *map = &base->sigmap;
5068585484eSchristos 	struct evmap_signal *ctx;
5078585484eSchristos 	struct event *ev;
5088585484eSchristos 
509b8ecfcfeSchristos 	if (sig < 0 || sig >= map->nentries)
510b8ecfcfeSchristos 		return;
5118585484eSchristos 	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
5128585484eSchristos 
513b8ecfcfeSchristos 	if (!ctx)
514b8ecfcfeSchristos 		return;
5158585484eSchristos 	LIST_FOREACH(ev, &ctx->events, ev_signal_next)
5168585484eSchristos 		event_active_nolock_(ev, EV_SIGNAL, ncalls);
5178585484eSchristos }
5188585484eSchristos 
5198585484eSchristos void *
5208585484eSchristos evmap_io_get_fdinfo_(struct event_io_map *map, evutil_socket_t fd)
5218585484eSchristos {
5228585484eSchristos 	struct evmap_io *ctx;
5238585484eSchristos 	GET_IO_SLOT(ctx, map, fd, evmap_io);
5248585484eSchristos 	if (ctx)
5258585484eSchristos 		return ((char*)ctx) + sizeof(struct evmap_io);
5268585484eSchristos 	else
5278585484eSchristos 		return NULL;
5288585484eSchristos }
5298585484eSchristos 
5308585484eSchristos /* Callback type for evmap_io_foreach_fd */
5318585484eSchristos typedef int (*evmap_io_foreach_fd_cb)(
5328585484eSchristos 	struct event_base *, evutil_socket_t, struct evmap_io *, void *);
5338585484eSchristos 
5348585484eSchristos /* Multipurpose helper function: Iterate over every file descriptor event_base
5358585484eSchristos  * for which we could have EV_READ or EV_WRITE events.  For each such fd, call
5368585484eSchristos  * fn(base, signum, evmap_io, arg), where fn is the user-provided
5378585484eSchristos  * function, base is the event_base, signum is the signal number, evmap_io
5388585484eSchristos  * is an evmap_io structure containing a list of events pending on the
5398585484eSchristos  * file descriptor, and arg is the user-supplied argument.
5408585484eSchristos  *
5418585484eSchristos  * If fn returns 0, continue on to the next signal. Otherwise, return the same
5428585484eSchristos  * value that fn returned.
5438585484eSchristos  *
5448585484eSchristos  * Note that there is no guarantee that the file descriptors will be processed
5458585484eSchristos  * in any particular order.
5468585484eSchristos  */
5478585484eSchristos static int
5488585484eSchristos evmap_io_foreach_fd(struct event_base *base,
5498585484eSchristos     evmap_io_foreach_fd_cb fn,
5508585484eSchristos     void *arg)
5518585484eSchristos {
5528585484eSchristos 	evutil_socket_t fd;
5538585484eSchristos 	struct event_io_map *iomap = &base->io;
5548585484eSchristos 	int r = 0;
5558585484eSchristos #ifdef EVMAP_USE_HT
5568585484eSchristos 	struct event_map_entry **mapent;
5578585484eSchristos 	HT_FOREACH(mapent, event_io_map, iomap) {
5588585484eSchristos 		struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
5598585484eSchristos 		fd = (*mapent)->fd;
5608585484eSchristos #else
5618585484eSchristos 	for (fd = 0; fd < iomap->nentries; ++fd) {
5628585484eSchristos 		struct evmap_io *ctx = iomap->entries[fd];
5638585484eSchristos 		if (!ctx)
5648585484eSchristos 			continue;
5658585484eSchristos #endif
5668585484eSchristos 		if ((r = fn(base, fd, ctx, arg)))
5678585484eSchristos 			break;
5688585484eSchristos 	}
5698585484eSchristos 	return r;
5708585484eSchristos }
5718585484eSchristos 
5728585484eSchristos /* Callback type for evmap_signal_foreach_signal */
5738585484eSchristos typedef int (*evmap_signal_foreach_signal_cb)(
5748585484eSchristos 	struct event_base *, int, struct evmap_signal *, void *);
5758585484eSchristos 
5768585484eSchristos /* Multipurpose helper function: Iterate over every signal number in the
5778585484eSchristos  * event_base for which we could have signal events.  For each such signal,
5788585484eSchristos  * call fn(base, signum, evmap_signal, arg), where fn is the user-provided
5798585484eSchristos  * function, base is the event_base, signum is the signal number, evmap_signal
5808585484eSchristos  * is an evmap_signal structure containing a list of events pending on the
5818585484eSchristos  * signal, and arg is the user-supplied argument.
5828585484eSchristos  *
5838585484eSchristos  * If fn returns 0, continue on to the next signal. Otherwise, return the same
5848585484eSchristos  * value that fn returned.
5858585484eSchristos  */
5868585484eSchristos static int
5878585484eSchristos evmap_signal_foreach_signal(struct event_base *base,
5888585484eSchristos     evmap_signal_foreach_signal_cb fn,
5898585484eSchristos     void *arg)
5908585484eSchristos {
5918585484eSchristos 	struct event_signal_map *sigmap = &base->sigmap;
5928585484eSchristos 	int r = 0;
5938585484eSchristos 	int signum;
5948585484eSchristos 
5958585484eSchristos 	for (signum = 0; signum < sigmap->nentries; ++signum) {
5968585484eSchristos 		struct evmap_signal *ctx = sigmap->entries[signum];
5978585484eSchristos 		if (!ctx)
5988585484eSchristos 			continue;
5998585484eSchristos 		if ((r = fn(base, signum, ctx, arg)))
6008585484eSchristos 			break;
6018585484eSchristos 	}
6028585484eSchristos 	return r;
6038585484eSchristos }
6048585484eSchristos 
6058585484eSchristos /* Helper for evmap_reinit_: tell the backend to add every fd for which we have
6068585484eSchristos  * pending events, with the appropriate combination of EV_READ, EV_WRITE, and
6078585484eSchristos  * EV_ET. */
6088585484eSchristos static int
6098585484eSchristos evmap_io_reinit_iter_fn(struct event_base *base, evutil_socket_t fd,
6108585484eSchristos     struct evmap_io *ctx, void *arg)
6118585484eSchristos {
6128585484eSchristos 	const struct eventop *evsel = base->evsel;
6138585484eSchristos 	void *extra;
6148585484eSchristos 	int *result = arg;
6158585484eSchristos 	short events = 0;
6168585484eSchristos 	struct event *ev;
6178585484eSchristos 	EVUTIL_ASSERT(ctx);
6188585484eSchristos 
6198585484eSchristos 	extra = ((char*)ctx) + sizeof(struct evmap_io);
6208585484eSchristos 	if (ctx->nread)
6218585484eSchristos 		events |= EV_READ;
622b8ecfcfeSchristos 	if (ctx->nwrite)
6238585484eSchristos 		events |= EV_WRITE;
624b8ecfcfeSchristos 	if (ctx->nclose)
625b8ecfcfeSchristos 		events |= EV_CLOSED;
6268585484eSchristos 	if (evsel->fdinfo_len)
6278585484eSchristos 		memset(extra, 0, evsel->fdinfo_len);
6288585484eSchristos 	if (events &&
6298585484eSchristos 	    (ev = LIST_FIRST(&ctx->events)) &&
6308585484eSchristos 	    (ev->ev_events & EV_ET))
6318585484eSchristos 		events |= EV_ET;
6328585484eSchristos 	if (evsel->add(base, fd, 0, events, extra) == -1)
6338585484eSchristos 		*result = -1;
6348585484eSchristos 
6358585484eSchristos 	return 0;
6368585484eSchristos }
6378585484eSchristos 
6388585484eSchristos /* Helper for evmap_reinit_: tell the backend to add every signal for which we
6398585484eSchristos  * have pending events.  */
6408585484eSchristos static int
6418585484eSchristos evmap_signal_reinit_iter_fn(struct event_base *base,
6428585484eSchristos     int signum, struct evmap_signal *ctx, void *arg)
6438585484eSchristos {
6448585484eSchristos 	const struct eventop *evsel = base->evsigsel;
6458585484eSchristos 	int *result = arg;
6468585484eSchristos 
6478585484eSchristos 	if (!LIST_EMPTY(&ctx->events)) {
6488585484eSchristos 		if (evsel->add(base, signum, 0, EV_SIGNAL, NULL) == -1)
6498585484eSchristos 			*result = -1;
6508585484eSchristos 	}
6518585484eSchristos 	return 0;
6528585484eSchristos }
6538585484eSchristos 
6548585484eSchristos int
6558585484eSchristos evmap_reinit_(struct event_base *base)
6568585484eSchristos {
6578585484eSchristos 	int result = 0;
6588585484eSchristos 
6598585484eSchristos 	evmap_io_foreach_fd(base, evmap_io_reinit_iter_fn, &result);
6608585484eSchristos 	if (result < 0)
6618585484eSchristos 		return -1;
6628585484eSchristos 	evmap_signal_foreach_signal(base, evmap_signal_reinit_iter_fn, &result);
6638585484eSchristos 	if (result < 0)
6648585484eSchristos 		return -1;
6658585484eSchristos 	return 0;
6668585484eSchristos }
6678585484eSchristos 
6688585484eSchristos /* Helper for evmap_delete_all_: delete every event in an event_dlist. */
6698585484eSchristos static int
6708585484eSchristos delete_all_in_dlist(struct event_dlist *dlist)
6718585484eSchristos {
6728585484eSchristos 	struct event *ev;
6738585484eSchristos 	while ((ev = LIST_FIRST(dlist)))
6748585484eSchristos 		event_del(ev);
6758585484eSchristos 	return 0;
6768585484eSchristos }
6778585484eSchristos 
6788585484eSchristos /* Helper for evmap_delete_all_: delete every event pending on an fd. */
6798585484eSchristos static int
6808585484eSchristos evmap_io_delete_all_iter_fn(struct event_base *base, evutil_socket_t fd,
6818585484eSchristos     struct evmap_io *io_info, void *arg)
6828585484eSchristos {
6838585484eSchristos 	return delete_all_in_dlist(&io_info->events);
6848585484eSchristos }
6858585484eSchristos 
6868585484eSchristos /* Helper for evmap_delete_all_: delete every event pending on a signal. */
6878585484eSchristos static int
6888585484eSchristos evmap_signal_delete_all_iter_fn(struct event_base *base, int signum,
6898585484eSchristos     struct evmap_signal *sig_info, void *arg)
6908585484eSchristos {
6918585484eSchristos 	return delete_all_in_dlist(&sig_info->events);
6928585484eSchristos }
6938585484eSchristos 
6948585484eSchristos void
6958585484eSchristos evmap_delete_all_(struct event_base *base)
6968585484eSchristos {
6978585484eSchristos 	evmap_signal_foreach_signal(base, evmap_signal_delete_all_iter_fn, NULL);
6988585484eSchristos 	evmap_io_foreach_fd(base, evmap_io_delete_all_iter_fn, NULL);
6998585484eSchristos }
7008585484eSchristos 
7018585484eSchristos /** Per-fd structure for use with changelists.  It keeps track, for each fd or
7028585484eSchristos  * signal using the changelist, of where its entry in the changelist is.
7038585484eSchristos  */
7048585484eSchristos struct event_changelist_fdinfo {
7058585484eSchristos 	int idxplus1; /* this is the index +1, so that memset(0) will make it
7068585484eSchristos 		       * a no-such-element */
7078585484eSchristos };
7088585484eSchristos 
7098585484eSchristos void
7108585484eSchristos event_changelist_init_(struct event_changelist *changelist)
7118585484eSchristos {
7128585484eSchristos 	changelist->changes = NULL;
7138585484eSchristos 	changelist->changes_size = 0;
7148585484eSchristos 	changelist->n_changes = 0;
7158585484eSchristos }
7168585484eSchristos 
7178585484eSchristos /** Helper: return the changelist_fdinfo corresponding to a given change. */
7188585484eSchristos static inline struct event_changelist_fdinfo *
7198585484eSchristos event_change_get_fdinfo(struct event_base *base,
7208585484eSchristos     const struct event_change *change)
7218585484eSchristos {
7228585484eSchristos 	char *ptr;
7238585484eSchristos 	if (change->read_change & EV_CHANGE_SIGNAL) {
7248585484eSchristos 		struct evmap_signal *ctx;
7258585484eSchristos 		GET_SIGNAL_SLOT(ctx, &base->sigmap, change->fd, evmap_signal);
7268585484eSchristos 		ptr = ((char*)ctx) + sizeof(struct evmap_signal);
7278585484eSchristos 	} else {
7288585484eSchristos 		struct evmap_io *ctx;
7298585484eSchristos 		GET_IO_SLOT(ctx, &base->io, change->fd, evmap_io);
7308585484eSchristos 		ptr = ((char*)ctx) + sizeof(struct evmap_io);
7318585484eSchristos 	}
7328585484eSchristos 	return (void*)ptr;
7338585484eSchristos }
7348585484eSchristos 
7358585484eSchristos /** Callback helper for event_changelist_assert_ok */
7368585484eSchristos static int
7378585484eSchristos event_changelist_assert_ok_foreach_iter_fn(
7388585484eSchristos 	struct event_base *base,
7398585484eSchristos 	evutil_socket_t fd, struct evmap_io *io, void *arg)
7408585484eSchristos {
7418585484eSchristos 	struct event_changelist *changelist = &base->changelist;
7428585484eSchristos 	struct event_changelist_fdinfo *f;
7438585484eSchristos 	f = (void*)
7448585484eSchristos 	    ( ((char*)io) + sizeof(struct evmap_io) );
7458585484eSchristos 	if (f->idxplus1) {
7468585484eSchristos 		struct event_change *c = &changelist->changes[f->idxplus1 - 1];
7478585484eSchristos 		EVUTIL_ASSERT(c->fd == fd);
7488585484eSchristos 	}
7498585484eSchristos 	return 0;
7508585484eSchristos }
7518585484eSchristos 
7528585484eSchristos /** Make sure that the changelist is consistent with the evmap structures. */
7538585484eSchristos static void
7548585484eSchristos event_changelist_assert_ok(struct event_base *base)
7558585484eSchristos {
7568585484eSchristos 	int i;
7578585484eSchristos 	struct event_changelist *changelist = &base->changelist;
7588585484eSchristos 
7598585484eSchristos 	EVUTIL_ASSERT(changelist->changes_size >= changelist->n_changes);
7608585484eSchristos 	for (i = 0; i < changelist->n_changes; ++i) {
7618585484eSchristos 		struct event_change *c = &changelist->changes[i];
7628585484eSchristos 		struct event_changelist_fdinfo *f;
7638585484eSchristos 		EVUTIL_ASSERT(c->fd >= 0);
7648585484eSchristos 		f = event_change_get_fdinfo(base, c);
7658585484eSchristos 		EVUTIL_ASSERT(f);
7668585484eSchristos 		EVUTIL_ASSERT(f->idxplus1 == i + 1);
7678585484eSchristos 	}
7688585484eSchristos 
7698585484eSchristos 	evmap_io_foreach_fd(base,
7708585484eSchristos 	    event_changelist_assert_ok_foreach_iter_fn,
7718585484eSchristos 	    NULL);
7728585484eSchristos }
7738585484eSchristos 
7748585484eSchristos #ifdef DEBUG_CHANGELIST
7758585484eSchristos #define event_changelist_check(base)  event_changelist_assert_ok((base))
7768585484eSchristos #else
7778585484eSchristos #define event_changelist_check(base)  ((void)0)
7788585484eSchristos #endif
7798585484eSchristos 
7808585484eSchristos void
7818585484eSchristos event_changelist_remove_all_(struct event_changelist *changelist,
7828585484eSchristos     struct event_base *base)
7838585484eSchristos {
7848585484eSchristos 	int i;
7858585484eSchristos 
7868585484eSchristos 	event_changelist_check(base);
7878585484eSchristos 
7888585484eSchristos 	for (i = 0; i < changelist->n_changes; ++i) {
7898585484eSchristos 		struct event_change *ch = &changelist->changes[i];
7908585484eSchristos 		struct event_changelist_fdinfo *fdinfo =
7918585484eSchristos 		    event_change_get_fdinfo(base, ch);
7928585484eSchristos 		EVUTIL_ASSERT(fdinfo->idxplus1 == i + 1);
7938585484eSchristos 		fdinfo->idxplus1 = 0;
7948585484eSchristos 	}
7958585484eSchristos 
7968585484eSchristos 	changelist->n_changes = 0;
7978585484eSchristos 
7988585484eSchristos 	event_changelist_check(base);
7998585484eSchristos }
8008585484eSchristos 
8018585484eSchristos void
8028585484eSchristos event_changelist_freemem_(struct event_changelist *changelist)
8038585484eSchristos {
8048585484eSchristos 	if (changelist->changes)
8058585484eSchristos 		mm_free(changelist->changes);
8068585484eSchristos 	event_changelist_init_(changelist); /* zero it all out. */
8078585484eSchristos }
8088585484eSchristos 
8098585484eSchristos /** Increase the size of 'changelist' to hold more changes. */
8108585484eSchristos static int
8118585484eSchristos event_changelist_grow(struct event_changelist *changelist)
8128585484eSchristos {
8138585484eSchristos 	int new_size;
8148585484eSchristos 	struct event_change *new_changes;
8158585484eSchristos 	if (changelist->changes_size < 64)
8168585484eSchristos 		new_size = 64;
8178585484eSchristos 	else
8188585484eSchristos 		new_size = changelist->changes_size * 2;
8198585484eSchristos 
8208585484eSchristos 	new_changes = mm_realloc(changelist->changes,
8218585484eSchristos 	    new_size * sizeof(struct event_change));
8228585484eSchristos 
8238585484eSchristos 	if (EVUTIL_UNLIKELY(new_changes == NULL))
8248585484eSchristos 		return (-1);
8258585484eSchristos 
8268585484eSchristos 	changelist->changes = new_changes;
8278585484eSchristos 	changelist->changes_size = new_size;
8288585484eSchristos 
8298585484eSchristos 	return (0);
8308585484eSchristos }
8318585484eSchristos 
8328585484eSchristos /** Return a pointer to the changelist entry for the file descriptor or signal
8338585484eSchristos  * 'fd', whose fdinfo is 'fdinfo'.  If none exists, construct it, setting its
8348585484eSchristos  * old_events field to old_events.
8358585484eSchristos  */
8368585484eSchristos static struct event_change *
8378585484eSchristos event_changelist_get_or_construct(struct event_changelist *changelist,
8388585484eSchristos     evutil_socket_t fd,
8398585484eSchristos     short old_events,
8408585484eSchristos     struct event_changelist_fdinfo *fdinfo)
8418585484eSchristos {
8428585484eSchristos 	struct event_change *change;
8438585484eSchristos 
8448585484eSchristos 	if (fdinfo->idxplus1 == 0) {
8458585484eSchristos 		int idx;
8468585484eSchristos 		EVUTIL_ASSERT(changelist->n_changes <= changelist->changes_size);
8478585484eSchristos 
8488585484eSchristos 		if (changelist->n_changes == changelist->changes_size) {
8498585484eSchristos 			if (event_changelist_grow(changelist) < 0)
8508585484eSchristos 				return NULL;
8518585484eSchristos 		}
8528585484eSchristos 
8538585484eSchristos 		idx = changelist->n_changes++;
8548585484eSchristos 		change = &changelist->changes[idx];
8558585484eSchristos 		fdinfo->idxplus1 = idx + 1;
8568585484eSchristos 
8578585484eSchristos 		memset(change, 0, sizeof(struct event_change));
8588585484eSchristos 		change->fd = fd;
8598585484eSchristos 		change->old_events = old_events;
8608585484eSchristos 	} else {
8618585484eSchristos 		change = &changelist->changes[fdinfo->idxplus1 - 1];
8628585484eSchristos 		EVUTIL_ASSERT(change->fd == fd);
8638585484eSchristos 	}
8648585484eSchristos 	return change;
8658585484eSchristos }
8668585484eSchristos 
8678585484eSchristos int
8688585484eSchristos event_changelist_add_(struct event_base *base, evutil_socket_t fd, short old, short events,
8698585484eSchristos     void *p)
8708585484eSchristos {
8718585484eSchristos 	struct event_changelist *changelist = &base->changelist;
8728585484eSchristos 	struct event_changelist_fdinfo *fdinfo = p;
8738585484eSchristos 	struct event_change *change;
874*eabc0478Schristos 	ev_uint8_t evchange = EV_CHANGE_ADD | (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
8758585484eSchristos 
8768585484eSchristos 	event_changelist_check(base);
8778585484eSchristos 
8788585484eSchristos 	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
8798585484eSchristos 	if (!change)
8808585484eSchristos 		return -1;
8818585484eSchristos 
8828585484eSchristos 	/* An add replaces any previous delete, but doesn't result in a no-op,
8838585484eSchristos 	 * since the delete might fail (because the fd had been closed since
8848585484eSchristos 	 * the last add, for instance. */
8858585484eSchristos 
886*eabc0478Schristos 	if (events & (EV_READ|EV_SIGNAL))
887*eabc0478Schristos 		change->read_change = evchange;
888*eabc0478Schristos 	if (events & EV_WRITE)
889*eabc0478Schristos 		change->write_change = evchange;
890*eabc0478Schristos 	if (events & EV_CLOSED)
891*eabc0478Schristos 		change->close_change = evchange;
8928585484eSchristos 
8938585484eSchristos 	event_changelist_check(base);
8948585484eSchristos 	return (0);
8958585484eSchristos }
8968585484eSchristos 
8978585484eSchristos int
8988585484eSchristos event_changelist_del_(struct event_base *base, evutil_socket_t fd, short old, short events,
8998585484eSchristos     void *p)
9008585484eSchristos {
9018585484eSchristos 	struct event_changelist *changelist = &base->changelist;
9028585484eSchristos 	struct event_changelist_fdinfo *fdinfo = p;
9038585484eSchristos 	struct event_change *change;
904*eabc0478Schristos 	ev_uint8_t del = EV_CHANGE_DEL | (events & EV_ET);
9058585484eSchristos 
9068585484eSchristos 	event_changelist_check(base);
9078585484eSchristos 	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
9088585484eSchristos 	event_changelist_check(base);
9098585484eSchristos 	if (!change)
9108585484eSchristos 		return -1;
9118585484eSchristos 
9128585484eSchristos 	/* A delete on an event set that doesn't contain the event to be
9138585484eSchristos 	   deleted produces a no-op.  This effectively emoves any previous
9148585484eSchristos 	   uncommitted add, rather than replacing it: on those platforms where
9158585484eSchristos 	   "add, delete, dispatch" is not the same as "no-op, dispatch", we
9168585484eSchristos 	   want the no-op behavior.
9178585484eSchristos 
9188585484eSchristos 	   If we have a no-op item, we could remove it it from the list
9198585484eSchristos 	   entirely, but really there's not much point: skipping the no-op
9208585484eSchristos 	   change when we do the dispatch later is far cheaper than rejuggling
9218585484eSchristos 	   the array now.
9228585484eSchristos 
9238585484eSchristos 	   As this stands, it also lets through deletions of events that are
9248585484eSchristos 	   not currently set.
9258585484eSchristos 	 */
9268585484eSchristos 
9278585484eSchristos 	if (events & (EV_READ|EV_SIGNAL)) {
9288585484eSchristos 		if (!(change->old_events & (EV_READ | EV_SIGNAL)))
9298585484eSchristos 			change->read_change = 0;
9308585484eSchristos 		else
931*eabc0478Schristos 			change->read_change = del;
9328585484eSchristos 	}
9338585484eSchristos 	if (events & EV_WRITE) {
9348585484eSchristos 		if (!(change->old_events & EV_WRITE))
9358585484eSchristos 			change->write_change = 0;
9368585484eSchristos 		else
937*eabc0478Schristos 			change->write_change = del;
9388585484eSchristos 	}
939b8ecfcfeSchristos 	if (events & EV_CLOSED) {
940b8ecfcfeSchristos 		if (!(change->old_events & EV_CLOSED))
941b8ecfcfeSchristos 			change->close_change = 0;
942b8ecfcfeSchristos 		else
943*eabc0478Schristos 			change->close_change = del;
944b8ecfcfeSchristos 	}
9458585484eSchristos 
9468585484eSchristos 	event_changelist_check(base);
9478585484eSchristos 	return (0);
9488585484eSchristos }
9498585484eSchristos 
9508585484eSchristos /* Helper for evmap_check_integrity_: verify that all of the events pending on
9518585484eSchristos  * given fd are set up correctly, and that the nread and nwrite counts on that
9528585484eSchristos  * fd are correct. */
9538585484eSchristos static int
9548585484eSchristos evmap_io_check_integrity_fn(struct event_base *base, evutil_socket_t fd,
9558585484eSchristos     struct evmap_io *io_info, void *arg)
9568585484eSchristos {
9578585484eSchristos 	struct event *ev;
958b8ecfcfeSchristos 	int n_read = 0, n_write = 0, n_close = 0;
9598585484eSchristos 
9608585484eSchristos 	/* First, make sure the list itself isn't corrupt. Otherwise,
9618585484eSchristos 	 * running LIST_FOREACH could be an exciting adventure. */
9628585484eSchristos 	EVUTIL_ASSERT_LIST_OK(&io_info->events, event, ev_io_next);
9638585484eSchristos 
9648585484eSchristos 	LIST_FOREACH(ev, &io_info->events, ev_io_next) {
9658585484eSchristos 		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
9668585484eSchristos 		EVUTIL_ASSERT(ev->ev_fd == fd);
9678585484eSchristos 		EVUTIL_ASSERT(!(ev->ev_events & EV_SIGNAL));
968b8ecfcfeSchristos 		EVUTIL_ASSERT((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)));
9698585484eSchristos 		if (ev->ev_events & EV_READ)
9708585484eSchristos 			++n_read;
9718585484eSchristos 		if (ev->ev_events & EV_WRITE)
9728585484eSchristos 			++n_write;
973b8ecfcfeSchristos 		if (ev->ev_events & EV_CLOSED)
974b8ecfcfeSchristos 			++n_close;
9758585484eSchristos 	}
9768585484eSchristos 
9778585484eSchristos 	EVUTIL_ASSERT(n_read == io_info->nread);
9788585484eSchristos 	EVUTIL_ASSERT(n_write == io_info->nwrite);
979b8ecfcfeSchristos 	EVUTIL_ASSERT(n_close == io_info->nclose);
9808585484eSchristos 
9818585484eSchristos 	return 0;
9828585484eSchristos }
9838585484eSchristos 
9848585484eSchristos /* Helper for evmap_check_integrity_: verify that all of the events pending
9858585484eSchristos  * on given signal are set up correctly. */
9868585484eSchristos static int
9878585484eSchristos evmap_signal_check_integrity_fn(struct event_base *base,
9888585484eSchristos     int signum, struct evmap_signal *sig_info, void *arg)
9898585484eSchristos {
9908585484eSchristos 	struct event *ev;
9918585484eSchristos 	/* First, make sure the list itself isn't corrupt. */
9928585484eSchristos 	EVUTIL_ASSERT_LIST_OK(&sig_info->events, event, ev_signal_next);
9938585484eSchristos 
9948585484eSchristos 	LIST_FOREACH(ev, &sig_info->events, ev_io_next) {
9958585484eSchristos 		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
9968585484eSchristos 		EVUTIL_ASSERT(ev->ev_fd == signum);
9978585484eSchristos 		EVUTIL_ASSERT((ev->ev_events & EV_SIGNAL));
998b8ecfcfeSchristos 		EVUTIL_ASSERT(!(ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)));
9998585484eSchristos 	}
10008585484eSchristos 	return 0;
10018585484eSchristos }
10028585484eSchristos 
10038585484eSchristos void
10048585484eSchristos evmap_check_integrity_(struct event_base *base)
10058585484eSchristos {
10068585484eSchristos 	evmap_io_foreach_fd(base, evmap_io_check_integrity_fn, NULL);
10078585484eSchristos 	evmap_signal_foreach_signal(base, evmap_signal_check_integrity_fn, NULL);
10088585484eSchristos 
10098585484eSchristos 	if (base->evsel->add == event_changelist_add_)
10108585484eSchristos 		event_changelist_assert_ok(base);
10118585484eSchristos }
10128585484eSchristos 
10138585484eSchristos /* Helper type for evmap_foreach_event_: Bundles a function to call on every
10148585484eSchristos  * event, and the user-provided void* to use as its third argument. */
10158585484eSchristos struct evmap_foreach_event_helper {
10168585484eSchristos 	event_base_foreach_event_cb fn;
10178585484eSchristos 	void *arg;
10188585484eSchristos };
10198585484eSchristos 
10208585484eSchristos /* Helper for evmap_foreach_event_: calls a provided function on every event
10218585484eSchristos  * pending on a given fd.  */
10228585484eSchristos static int
10238585484eSchristos evmap_io_foreach_event_fn(struct event_base *base, evutil_socket_t fd,
10248585484eSchristos     struct evmap_io *io_info, void *arg)
10258585484eSchristos {
10268585484eSchristos 	struct evmap_foreach_event_helper *h = arg;
10278585484eSchristos 	struct event *ev;
10288585484eSchristos 	int r;
10298585484eSchristos 	LIST_FOREACH(ev, &io_info->events, ev_io_next) {
10308585484eSchristos 		if ((r = h->fn(base, ev, h->arg)))
10318585484eSchristos 			return r;
10328585484eSchristos 	}
10338585484eSchristos 	return 0;
10348585484eSchristos }
10358585484eSchristos 
10368585484eSchristos /* Helper for evmap_foreach_event_: calls a provided function on every event
10378585484eSchristos  * pending on a given signal.  */
10388585484eSchristos static int
10398585484eSchristos evmap_signal_foreach_event_fn(struct event_base *base, int signum,
10408585484eSchristos     struct evmap_signal *sig_info, void *arg)
10418585484eSchristos {
10428585484eSchristos 	struct event *ev;
10438585484eSchristos 	struct evmap_foreach_event_helper *h = arg;
10448585484eSchristos 	int r;
10458585484eSchristos 	LIST_FOREACH(ev, &sig_info->events, ev_signal_next) {
10468585484eSchristos 		if ((r = h->fn(base, ev, h->arg)))
10478585484eSchristos 			return r;
10488585484eSchristos 	}
10498585484eSchristos 	return 0;
10508585484eSchristos }
10518585484eSchristos 
10528585484eSchristos int
10538585484eSchristos evmap_foreach_event_(struct event_base *base,
10548585484eSchristos     event_base_foreach_event_cb fn, void *arg)
10558585484eSchristos {
10568585484eSchristos 	struct evmap_foreach_event_helper h;
10578585484eSchristos 	int r;
10588585484eSchristos 	h.fn = fn;
10598585484eSchristos 	h.arg = arg;
10608585484eSchristos 	if ((r = evmap_io_foreach_fd(base, evmap_io_foreach_event_fn, &h)))
10618585484eSchristos 		return r;
10628585484eSchristos 	return evmap_signal_foreach_signal(base, evmap_signal_foreach_event_fn, &h);
10638585484eSchristos }
10648585484eSchristos 
1065