1/*	$NetBSD: evmap.c,v 1.5 2020/05/25 20:47:33 christos Exp $	*/
2
3/*
4 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#include "event2/event-config.h"
29#include "evconfig-private.h"
30
31#ifdef _WIN32
32#include <winsock2.h>
33#define WIN32_LEAN_AND_MEAN
34#include <windows.h>
35#undef WIN32_LEAN_AND_MEAN
36#endif
37#include <sys/types.h>
38#if !defined(_WIN32) && defined(EVENT__HAVE_SYS_TIME_H)
39#include <sys/time.h>
40#endif
41#include <sys/queue.h>
42#include <stdio.h>
43#include <stdlib.h>
44#ifndef _WIN32
45#include <unistd.h>
46#endif
47#include <errno.h>
48#include <signal.h>
49#include <string.h>
50#include <time.h>
51
52#include "event-internal.h"
53#include "evmap-internal.h"
54#include "mm-internal.h"
55#include "changelist-internal.h"
56
57/** An entry for an evmap_io list: notes all the events that want to read or
58	write on a given fd, and the number of each.
59  */
60struct evmap_io {
61	struct event_dlist events;
62	ev_uint16_t nread;
63	ev_uint16_t nwrite;
64	ev_uint16_t nclose;
65};
66
67/* An entry for an evmap_signal list: notes all the events that want to know
68   when a signal triggers. */
69struct evmap_signal {
70	struct event_dlist events;
71};
72
73/* On some platforms, fds start at 0 and increment by 1 as they are
74   allocated, and old numbers get used.  For these platforms, we
75   implement io maps just like signal maps: as an array of pointers to
76   struct evmap_io.  But on other platforms (windows), sockets are not
77   0-indexed, not necessarily consecutive, and not necessarily reused.
78   There, we use a hashtable to implement evmap_io.
79*/
80#ifdef EVMAP_USE_HT
81struct event_map_entry {
82	HT_ENTRY(event_map_entry) map_node;
83	evutil_socket_t fd;
84	union { /* This is a union in case we need to make more things that can
85			   be in the hashtable. */
86		struct evmap_io evmap_io;
87	} ent;
88};
89
90/* Helper used by the event_io_map hashtable code; tries to return a good hash
91 * of the fd in e->fd. */
92static inline unsigned
93hashsocket(struct event_map_entry *e)
94{
95	/* On win32, in practice, the low 2-3 bits of a SOCKET seem not to
96	 * matter.  Our hashtable implementation really likes low-order bits,
97	 * though, so let's do the rotate-and-add trick. */
98	unsigned h = (unsigned) e->fd;
99	h += (h >> 2) | (h << 30);
100	return h;
101}
102
103/* Helper used by the event_io_map hashtable code; returns true iff e1 and e2
104 * have the same e->fd. */
105static inline int
106eqsocket(struct event_map_entry *e1, struct event_map_entry *e2)
107{
108	return e1->fd == e2->fd;
109}
110
111HT_PROTOTYPE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket)
112HT_GENERATE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket,
113			0.5, mm_malloc, mm_realloc, mm_free)
114
115#define GET_IO_SLOT(x, map, slot, type)					\
116	do {								\
117		struct event_map_entry key_, *ent_;			\
118		key_.fd = slot;						\
119		ent_ = HT_FIND(event_io_map, map, &key_);		\
120		(x) = ent_ ? &ent_->ent.type : NULL;			\
121	} while (0);
122
123#define GET_IO_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
124	do {								\
125		struct event_map_entry key_, *ent_;			\
126		key_.fd = slot;						\
127		HT_FIND_OR_INSERT_(event_io_map, map_node, hashsocket, map, \
128		    event_map_entry, &key_, ptr,			\
129		    {							\
130			    ent_ = *ptr;				\
131		    },							\
132		    {							\
133			    ent_ = mm_calloc(1,sizeof(struct event_map_entry)+fdinfo_len); \
134			    if (EVUTIL_UNLIKELY(ent_ == NULL))		\
135				    return (-1);			\
136			    ent_->fd = slot;				\
137			    (ctor)(&ent_->ent.type);			\
138			    HT_FOI_INSERT_(map_node, map, &key_, ent_, ptr) \
139				});					\
140		(x) = &ent_->ent.type;					\
141	} while (0)
142
143void evmap_io_initmap_(struct event_io_map *ctx)
144{
145	HT_INIT(event_io_map, ctx);
146}
147
148void evmap_io_clear_(struct event_io_map *ctx)
149{
150	struct event_map_entry **ent, **next, *this;
151	for (ent = HT_START(event_io_map, ctx); ent; ent = next) {
152		this = *ent;
153		next = HT_NEXT_RMV(event_io_map, ctx, ent);
154		mm_free(this);
155	}
156	HT_CLEAR(event_io_map, ctx); /* remove all storage held by the ctx. */
157}
158#endif
159
160/* Set the variable 'x' to the field in event_map 'map' with fields of type
161   'struct type *' corresponding to the fd or signal 'slot'.  Set 'x' to NULL
162   if there are no entries for 'slot'.  Does no bounds-checking. */
163#define GET_SIGNAL_SLOT(x, map, slot, type)			\
164	(x) = (struct type *)((map)->entries[slot])
165/* As GET_SLOT, but construct the entry for 'slot' if it is not present,
166   by allocating enough memory for a 'struct type', and initializing the new
167   value by calling the function 'ctor' on it.  Makes the function
168   return -1 on allocation failure.
169 */
170#define GET_SIGNAL_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)	\
171	do {								\
172		if ((map)->entries[slot] == NULL) {			\
173			(map)->entries[slot] =				\
174			    mm_calloc(1,sizeof(struct type)+fdinfo_len); \
175			if (EVUTIL_UNLIKELY((map)->entries[slot] == NULL)) \
176				return (-1);				\
177			(ctor)((struct type *)(map)->entries[slot]);	\
178		}							\
179		(x) = (struct type *)((map)->entries[slot]);		\
180	} while (0)
181
182/* If we aren't using hashtables, then define the IO_SLOT macros and functions
183   as thin aliases over the SIGNAL_SLOT versions. */
184#ifndef EVMAP_USE_HT
185#define GET_IO_SLOT(x,map,slot,type) GET_SIGNAL_SLOT(x,map,slot,type)
186#define GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)	\
187	GET_SIGNAL_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)
188#define FDINFO_OFFSET sizeof(struct evmap_io)
189void
190evmap_io_initmap_(struct event_io_map* ctx)
191{
192	evmap_signal_initmap_(ctx);
193}
194void
195evmap_io_clear_(struct event_io_map* ctx)
196{
197	evmap_signal_clear_(ctx);
198}
199#endif
200
201
202/** Expand 'map' with new entries of width 'msize' until it is big enough
203	to store a value in 'slot'.
204 */
205static int
206evmap_make_space(struct event_signal_map *map, int slot, int msize)
207{
208	if (map->nentries <= slot) {
209		int nentries = map->nentries ? map->nentries : 32;
210		void **tmp;
211
212		while (nentries <= slot)
213			nentries <<= 1;
214
215		tmp = (void **)mm_realloc(map->entries, nentries * msize);
216		if (tmp == NULL)
217			return (-1);
218
219		memset(&tmp[map->nentries], 0,
220		    (nentries - map->nentries) * msize);
221
222		map->nentries = nentries;
223		map->entries = tmp;
224	}
225
226	return (0);
227}
228
229void
230evmap_signal_initmap_(struct event_signal_map *ctx)
231{
232	ctx->nentries = 0;
233	ctx->entries = NULL;
234}
235
236void
237evmap_signal_clear_(struct event_signal_map *ctx)
238{
239	if (ctx->entries != NULL) {
240		int i;
241		for (i = 0; i < ctx->nentries; ++i) {
242			if (ctx->entries[i] != NULL)
243				mm_free(ctx->entries[i]);
244		}
245		mm_free(ctx->entries);
246		ctx->entries = NULL;
247	}
248	ctx->nentries = 0;
249}
250
251
252/* code specific to file descriptors */
253
254/** Constructor for struct evmap_io */
255static void
256evmap_io_init(struct evmap_io *entry)
257{
258	LIST_INIT(&entry->events);
259	entry->nread = 0;
260	entry->nwrite = 0;
261	entry->nclose = 0;
262}
263
264
265/* return -1 on error, 0 on success if nothing changed in the event backend,
266 * and 1 on success if something did. */
267int
268evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
269{
270	const struct eventop *evsel = base->evsel;
271	struct event_io_map *io = &base->io;
272	struct evmap_io *ctx = NULL;
273	int nread, nwrite, nclose, retval = 0;
274	short res = 0, old = 0;
275	struct event *old_ev;
276
277	EVUTIL_ASSERT(fd == ev->ev_fd);
278
279	if (fd < 0)
280		return 0;
281
282#ifndef EVMAP_USE_HT
283	if (fd >= io->nentries) {
284		if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
285			return (-1);
286	}
287#endif
288	GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
289						 evsel->fdinfo_len);
290
291	nread = ctx->nread;
292	nwrite = ctx->nwrite;
293	nclose = ctx->nclose;
294
295	if (nread)
296		old |= EV_READ;
297	if (nwrite)
298		old |= EV_WRITE;
299	if (nclose)
300		old |= EV_CLOSED;
301
302	if (ev->ev_events & EV_READ) {
303		if (++nread == 1)
304			res |= EV_READ;
305	}
306	if (ev->ev_events & EV_WRITE) {
307		if (++nwrite == 1)
308			res |= EV_WRITE;
309	}
310	if (ev->ev_events & EV_CLOSED) {
311		if (++nclose == 1)
312			res |= EV_CLOSED;
313	}
314	if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
315		event_warnx("Too many events reading or writing on fd %d",
316		    (int)fd);
317		return -1;
318	}
319	if (EVENT_DEBUG_MODE_IS_ON() &&
320	    (old_ev = LIST_FIRST(&ctx->events)) &&
321	    (old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
322		event_warnx("Tried to mix edge-triggered and non-edge-triggered"
323		    " events on fd %d", (int)fd);
324		return -1;
325	}
326
327	if (res) {
328		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
329		/* XXX(niels): we cannot mix edge-triggered and
330		 * level-triggered, we should probably assert on
331		 * this. */
332		if (evsel->add(base, ev->ev_fd,
333			old, (ev->ev_events & EV_ET) | res, extra) == -1)
334			return (-1);
335		retval = 1;
336	}
337
338	ctx->nread = (ev_uint16_t) nread;
339	ctx->nwrite = (ev_uint16_t) nwrite;
340	ctx->nclose = (ev_uint16_t) nclose;
341	LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
342
343	return (retval);
344}
345
346/* return -1 on error, 0 on success if nothing changed in the event backend,
347 * and 1 on success if something did. */
348int
349evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev)
350{
351	const struct eventop *evsel = base->evsel;
352	struct event_io_map *io = &base->io;
353	struct evmap_io *ctx;
354	int nread, nwrite, nclose, retval = 0;
355	short res = 0, old = 0;
356
357	if (fd < 0)
358		return 0;
359
360	EVUTIL_ASSERT(fd == ev->ev_fd);
361
362#ifndef EVMAP_USE_HT
363	if (fd >= io->nentries)
364		return (-1);
365#endif
366
367	GET_IO_SLOT(ctx, io, fd, evmap_io);
368
369	nread = ctx->nread;
370	nwrite = ctx->nwrite;
371	nclose = ctx->nclose;
372
373	if (nread)
374		old |= EV_READ;
375	if (nwrite)
376		old |= EV_WRITE;
377	if (nclose)
378		old |= EV_CLOSED;
379
380	if (ev->ev_events & EV_READ) {
381		if (--nread == 0)
382			res |= EV_READ;
383		EVUTIL_ASSERT(nread >= 0);
384	}
385	if (ev->ev_events & EV_WRITE) {
386		if (--nwrite == 0)
387			res |= EV_WRITE;
388		EVUTIL_ASSERT(nwrite >= 0);
389	}
390	if (ev->ev_events & EV_CLOSED) {
391		if (--nclose == 0)
392			res |= EV_CLOSED;
393		EVUTIL_ASSERT(nclose >= 0);
394	}
395
396	if (res) {
397		void *extra = ((char*)ctx) + sizeof(struct evmap_io);
398		if (evsel->del(base, ev->ev_fd, old, res, extra) == -1) {
399			retval = -1;
400		} else {
401			retval = 1;
402		}
403	}
404
405	ctx->nread = nread;
406	ctx->nwrite = nwrite;
407	ctx->nclose = nclose;
408	LIST_REMOVE(ev, ev_io_next);
409
410	return (retval);
411}
412
413void
414evmap_io_active_(struct event_base *base, evutil_socket_t fd, short events)
415{
416	struct event_io_map *io = &base->io;
417	struct evmap_io *ctx;
418	struct event *ev;
419
420#ifndef EVMAP_USE_HT
421	if (fd < 0 || fd >= io->nentries)
422		return;
423#endif
424	GET_IO_SLOT(ctx, io, fd, evmap_io);
425
426	if (NULL == ctx)
427		return;
428	LIST_FOREACH(ev, &ctx->events, ev_io_next) {
429		if (ev->ev_events & events)
430			event_active_nolock_(ev, ev->ev_events & events, 1);
431	}
432}
433
434/* code specific to signals */
435
436static void
437evmap_signal_init(struct evmap_signal *entry)
438{
439	LIST_INIT(&entry->events);
440}
441
442
443int
444evmap_signal_add_(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 = NULL;
449
450	if (sig >= map->nentries) {
451		if (evmap_make_space(
452			map, sig, sizeof(struct evmap_signal *)) == -1)
453			return (-1);
454	}
455	GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
456	    base->evsigsel->fdinfo_len);
457
458	if (LIST_EMPTY(&ctx->events)) {
459		if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
460		    == -1)
461			return (-1);
462	}
463
464	LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);
465
466	return (1);
467}
468
469int
470evmap_signal_del_(struct event_base *base, int sig, struct event *ev)
471{
472	const struct eventop *evsel = base->evsigsel;
473	struct event_signal_map *map = &base->sigmap;
474	struct evmap_signal *ctx;
475
476	if (sig >= map->nentries)
477		return (-1);
478
479	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
480
481	LIST_REMOVE(ev, ev_signal_next);
482
483	if (LIST_FIRST(&ctx->events) == NULL) {
484		if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
485			return (-1);
486	}
487
488	return (1);
489}
490
491void
492evmap_signal_active_(struct event_base *base, evutil_socket_t sig, int ncalls)
493{
494	struct event_signal_map *map = &base->sigmap;
495	struct evmap_signal *ctx;
496	struct event *ev;
497
498	if (sig < 0 || sig >= map->nentries)
499		return;
500	GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
501
502	if (!ctx)
503		return;
504	LIST_FOREACH(ev, &ctx->events, ev_signal_next)
505		event_active_nolock_(ev, EV_SIGNAL, ncalls);
506}
507
508void *
509evmap_io_get_fdinfo_(struct event_io_map *map, evutil_socket_t fd)
510{
511	struct evmap_io *ctx;
512	GET_IO_SLOT(ctx, map, fd, evmap_io);
513	if (ctx)
514		return ((char*)ctx) + sizeof(struct evmap_io);
515	else
516		return NULL;
517}
518
519/* Callback type for evmap_io_foreach_fd */
520typedef int (*evmap_io_foreach_fd_cb)(
521	struct event_base *, evutil_socket_t, struct evmap_io *, void *);
522
523/* Multipurpose helper function: Iterate over every file descriptor event_base
524 * for which we could have EV_READ or EV_WRITE events.  For each such fd, call
525 * fn(base, signum, evmap_io, arg), where fn is the user-provided
526 * function, base is the event_base, signum is the signal number, evmap_io
527 * is an evmap_io structure containing a list of events pending on the
528 * file descriptor, and arg is the user-supplied argument.
529 *
530 * If fn returns 0, continue on to the next signal. Otherwise, return the same
531 * value that fn returned.
532 *
533 * Note that there is no guarantee that the file descriptors will be processed
534 * in any particular order.
535 */
536static int
537evmap_io_foreach_fd(struct event_base *base,
538    evmap_io_foreach_fd_cb fn,
539    void *arg)
540{
541	evutil_socket_t fd;
542	struct event_io_map *iomap = &base->io;
543	int r = 0;
544#ifdef EVMAP_USE_HT
545	struct event_map_entry **mapent;
546	HT_FOREACH(mapent, event_io_map, iomap) {
547		struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
548		fd = (*mapent)->fd;
549#else
550	for (fd = 0; fd < iomap->nentries; ++fd) {
551		struct evmap_io *ctx = iomap->entries[fd];
552		if (!ctx)
553			continue;
554#endif
555		if ((r = fn(base, fd, ctx, arg)))
556			break;
557	}
558	return r;
559}
560
561/* Callback type for evmap_signal_foreach_signal */
562typedef int (*evmap_signal_foreach_signal_cb)(
563	struct event_base *, int, struct evmap_signal *, void *);
564
565/* Multipurpose helper function: Iterate over every signal number in the
566 * event_base for which we could have signal events.  For each such signal,
567 * call fn(base, signum, evmap_signal, arg), where fn is the user-provided
568 * function, base is the event_base, signum is the signal number, evmap_signal
569 * is an evmap_signal structure containing a list of events pending on the
570 * signal, and arg is the user-supplied argument.
571 *
572 * If fn returns 0, continue on to the next signal. Otherwise, return the same
573 * value that fn returned.
574 */
575static int
576evmap_signal_foreach_signal(struct event_base *base,
577    evmap_signal_foreach_signal_cb fn,
578    void *arg)
579{
580	struct event_signal_map *sigmap = &base->sigmap;
581	int r = 0;
582	int signum;
583
584	for (signum = 0; signum < sigmap->nentries; ++signum) {
585		struct evmap_signal *ctx = sigmap->entries[signum];
586		if (!ctx)
587			continue;
588		if ((r = fn(base, signum, ctx, arg)))
589			break;
590	}
591	return r;
592}
593
594/* Helper for evmap_reinit_: tell the backend to add every fd for which we have
595 * pending events, with the appropriate combination of EV_READ, EV_WRITE, and
596 * EV_ET. */
597static int
598evmap_io_reinit_iter_fn(struct event_base *base, evutil_socket_t fd,
599    struct evmap_io *ctx, void *arg)
600{
601	const struct eventop *evsel = base->evsel;
602	void *extra;
603	int *result = arg;
604	short events = 0;
605	struct event *ev;
606	EVUTIL_ASSERT(ctx);
607
608	extra = ((char*)ctx) + sizeof(struct evmap_io);
609	if (ctx->nread)
610		events |= EV_READ;
611	if (ctx->nwrite)
612		events |= EV_WRITE;
613	if (ctx->nclose)
614		events |= EV_CLOSED;
615	if (evsel->fdinfo_len)
616		memset(extra, 0, evsel->fdinfo_len);
617	if (events &&
618	    (ev = LIST_FIRST(&ctx->events)) &&
619	    (ev->ev_events & EV_ET))
620		events |= EV_ET;
621	if (evsel->add(base, fd, 0, events, extra) == -1)
622		*result = -1;
623
624	return 0;
625}
626
627/* Helper for evmap_reinit_: tell the backend to add every signal for which we
628 * have pending events.  */
629static int
630evmap_signal_reinit_iter_fn(struct event_base *base,
631    int signum, struct evmap_signal *ctx, void *arg)
632{
633	const struct eventop *evsel = base->evsigsel;
634	int *result = arg;
635
636	if (!LIST_EMPTY(&ctx->events)) {
637		if (evsel->add(base, signum, 0, EV_SIGNAL, NULL) == -1)
638			*result = -1;
639	}
640	return 0;
641}
642
643int
644evmap_reinit_(struct event_base *base)
645{
646	int result = 0;
647
648	evmap_io_foreach_fd(base, evmap_io_reinit_iter_fn, &result);
649	if (result < 0)
650		return -1;
651	evmap_signal_foreach_signal(base, evmap_signal_reinit_iter_fn, &result);
652	if (result < 0)
653		return -1;
654	return 0;
655}
656
657/* Helper for evmap_delete_all_: delete every event in an event_dlist. */
658static int
659delete_all_in_dlist(struct event_dlist *dlist)
660{
661	struct event *ev;
662	while ((ev = LIST_FIRST(dlist)))
663		event_del(ev);
664	return 0;
665}
666
667/* Helper for evmap_delete_all_: delete every event pending on an fd. */
668static int
669evmap_io_delete_all_iter_fn(struct event_base *base, evutil_socket_t fd,
670    struct evmap_io *io_info, void *arg)
671{
672	return delete_all_in_dlist(&io_info->events);
673}
674
675/* Helper for evmap_delete_all_: delete every event pending on a signal. */
676static int
677evmap_signal_delete_all_iter_fn(struct event_base *base, int signum,
678    struct evmap_signal *sig_info, void *arg)
679{
680	return delete_all_in_dlist(&sig_info->events);
681}
682
683void
684evmap_delete_all_(struct event_base *base)
685{
686	evmap_signal_foreach_signal(base, evmap_signal_delete_all_iter_fn, NULL);
687	evmap_io_foreach_fd(base, evmap_io_delete_all_iter_fn, NULL);
688}
689
690/** Per-fd structure for use with changelists.  It keeps track, for each fd or
691 * signal using the changelist, of where its entry in the changelist is.
692 */
693struct event_changelist_fdinfo {
694	int idxplus1; /* this is the index +1, so that memset(0) will make it
695		       * a no-such-element */
696};
697
698void
699event_changelist_init_(struct event_changelist *changelist)
700{
701	changelist->changes = NULL;
702	changelist->changes_size = 0;
703	changelist->n_changes = 0;
704}
705
706/** Helper: return the changelist_fdinfo corresponding to a given change. */
707static inline struct event_changelist_fdinfo *
708event_change_get_fdinfo(struct event_base *base,
709    const struct event_change *change)
710{
711	char *ptr;
712	if (change->read_change & EV_CHANGE_SIGNAL) {
713		struct evmap_signal *ctx;
714		GET_SIGNAL_SLOT(ctx, &base->sigmap, change->fd, evmap_signal);
715		ptr = ((char*)ctx) + sizeof(struct evmap_signal);
716	} else {
717		struct evmap_io *ctx;
718		GET_IO_SLOT(ctx, &base->io, change->fd, evmap_io);
719		ptr = ((char*)ctx) + sizeof(struct evmap_io);
720	}
721	return (void*)ptr;
722}
723
724/** Callback helper for event_changelist_assert_ok */
725static int
726event_changelist_assert_ok_foreach_iter_fn(
727	struct event_base *base,
728	evutil_socket_t fd, struct evmap_io *io, void *arg)
729{
730	struct event_changelist *changelist = &base->changelist;
731	struct event_changelist_fdinfo *f;
732	f = (void*)
733	    ( ((char*)io) + sizeof(struct evmap_io) );
734	if (f->idxplus1) {
735		struct event_change *c = &changelist->changes[f->idxplus1 - 1];
736		EVUTIL_ASSERT(c->fd == fd);
737	}
738	return 0;
739}
740
741/** Make sure that the changelist is consistent with the evmap structures. */
742static void
743event_changelist_assert_ok(struct event_base *base)
744{
745	int i;
746	struct event_changelist *changelist = &base->changelist;
747
748	EVUTIL_ASSERT(changelist->changes_size >= changelist->n_changes);
749	for (i = 0; i < changelist->n_changes; ++i) {
750		struct event_change *c = &changelist->changes[i];
751		struct event_changelist_fdinfo *f;
752		EVUTIL_ASSERT(c->fd >= 0);
753		f = event_change_get_fdinfo(base, c);
754		EVUTIL_ASSERT(f);
755		EVUTIL_ASSERT(f->idxplus1 == i + 1);
756	}
757
758	evmap_io_foreach_fd(base,
759	    event_changelist_assert_ok_foreach_iter_fn,
760	    NULL);
761}
762
763#ifdef DEBUG_CHANGELIST
764#define event_changelist_check(base)  event_changelist_assert_ok((base))
765#else
766#define event_changelist_check(base)  ((void)0)
767#endif
768
769void
770event_changelist_remove_all_(struct event_changelist *changelist,
771    struct event_base *base)
772{
773	int i;
774
775	event_changelist_check(base);
776
777	for (i = 0; i < changelist->n_changes; ++i) {
778		struct event_change *ch = &changelist->changes[i];
779		struct event_changelist_fdinfo *fdinfo =
780		    event_change_get_fdinfo(base, ch);
781		EVUTIL_ASSERT(fdinfo->idxplus1 == i + 1);
782		fdinfo->idxplus1 = 0;
783	}
784
785	changelist->n_changes = 0;
786
787	event_changelist_check(base);
788}
789
790void
791event_changelist_freemem_(struct event_changelist *changelist)
792{
793	if (changelist->changes)
794		mm_free(changelist->changes);
795	event_changelist_init_(changelist); /* zero it all out. */
796}
797
798/** Increase the size of 'changelist' to hold more changes. */
799static int
800event_changelist_grow(struct event_changelist *changelist)
801{
802	int new_size;
803	struct event_change *new_changes;
804	if (changelist->changes_size < 64)
805		new_size = 64;
806	else
807		new_size = changelist->changes_size * 2;
808
809	new_changes = mm_realloc(changelist->changes,
810	    new_size * sizeof(struct event_change));
811
812	if (EVUTIL_UNLIKELY(new_changes == NULL))
813		return (-1);
814
815	changelist->changes = new_changes;
816	changelist->changes_size = new_size;
817
818	return (0);
819}
820
821/** Return a pointer to the changelist entry for the file descriptor or signal
822 * 'fd', whose fdinfo is 'fdinfo'.  If none exists, construct it, setting its
823 * old_events field to old_events.
824 */
825static struct event_change *
826event_changelist_get_or_construct(struct event_changelist *changelist,
827    evutil_socket_t fd,
828    short old_events,
829    struct event_changelist_fdinfo *fdinfo)
830{
831	struct event_change *change;
832
833	if (fdinfo->idxplus1 == 0) {
834		int idx;
835		EVUTIL_ASSERT(changelist->n_changes <= changelist->changes_size);
836
837		if (changelist->n_changes == changelist->changes_size) {
838			if (event_changelist_grow(changelist) < 0)
839				return NULL;
840		}
841
842		idx = changelist->n_changes++;
843		change = &changelist->changes[idx];
844		fdinfo->idxplus1 = idx + 1;
845
846		memset(change, 0, sizeof(struct event_change));
847		change->fd = fd;
848		change->old_events = old_events;
849	} else {
850		change = &changelist->changes[fdinfo->idxplus1 - 1];
851		EVUTIL_ASSERT(change->fd == fd);
852	}
853	return change;
854}
855
856int
857event_changelist_add_(struct event_base *base, evutil_socket_t fd, short old, short events,
858    void *p)
859{
860	struct event_changelist *changelist = &base->changelist;
861	struct event_changelist_fdinfo *fdinfo = p;
862	struct event_change *change;
863
864	event_changelist_check(base);
865
866	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
867	if (!change)
868		return -1;
869
870	/* An add replaces any previous delete, but doesn't result in a no-op,
871	 * since the delete might fail (because the fd had been closed since
872	 * the last add, for instance. */
873
874	if (events & (EV_READ|EV_SIGNAL)) {
875		change->read_change = EV_CHANGE_ADD |
876		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
877	}
878	if (events & EV_WRITE) {
879		change->write_change = EV_CHANGE_ADD |
880		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
881	}
882	if (events & EV_CLOSED) {
883		change->close_change = EV_CHANGE_ADD |
884		    (events & (EV_ET|EV_PERSIST|EV_SIGNAL));
885	}
886
887	event_changelist_check(base);
888	return (0);
889}
890
891int
892event_changelist_del_(struct event_base *base, evutil_socket_t fd, short old, short events,
893    void *p)
894{
895	struct event_changelist *changelist = &base->changelist;
896	struct event_changelist_fdinfo *fdinfo = p;
897	struct event_change *change;
898
899	event_changelist_check(base);
900	change = event_changelist_get_or_construct(changelist, fd, old, fdinfo);
901	event_changelist_check(base);
902	if (!change)
903		return -1;
904
905	/* A delete on an event set that doesn't contain the event to be
906	   deleted produces a no-op.  This effectively emoves any previous
907	   uncommitted add, rather than replacing it: on those platforms where
908	   "add, delete, dispatch" is not the same as "no-op, dispatch", we
909	   want the no-op behavior.
910
911	   If we have a no-op item, we could remove it it from the list
912	   entirely, but really there's not much point: skipping the no-op
913	   change when we do the dispatch later is far cheaper than rejuggling
914	   the array now.
915
916	   As this stands, it also lets through deletions of events that are
917	   not currently set.
918	 */
919
920	if (events & (EV_READ|EV_SIGNAL)) {
921		if (!(change->old_events & (EV_READ | EV_SIGNAL)))
922			change->read_change = 0;
923		else
924			change->read_change = EV_CHANGE_DEL;
925	}
926	if (events & EV_WRITE) {
927		if (!(change->old_events & EV_WRITE))
928			change->write_change = 0;
929		else
930			change->write_change = EV_CHANGE_DEL;
931	}
932	if (events & EV_CLOSED) {
933		if (!(change->old_events & EV_CLOSED))
934			change->close_change = 0;
935		else
936			change->close_change = EV_CHANGE_DEL;
937	}
938
939	event_changelist_check(base);
940	return (0);
941}
942
943/* Helper for evmap_check_integrity_: verify that all of the events pending on
944 * given fd are set up correctly, and that the nread and nwrite counts on that
945 * fd are correct. */
946static int
947evmap_io_check_integrity_fn(struct event_base *base, evutil_socket_t fd,
948    struct evmap_io *io_info, void *arg)
949{
950	struct event *ev;
951	int n_read = 0, n_write = 0, n_close = 0;
952
953	/* First, make sure the list itself isn't corrupt. Otherwise,
954	 * running LIST_FOREACH could be an exciting adventure. */
955	EVUTIL_ASSERT_LIST_OK(&io_info->events, event, ev_io_next);
956
957	LIST_FOREACH(ev, &io_info->events, ev_io_next) {
958		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
959		EVUTIL_ASSERT(ev->ev_fd == fd);
960		EVUTIL_ASSERT(!(ev->ev_events & EV_SIGNAL));
961		EVUTIL_ASSERT((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)));
962		if (ev->ev_events & EV_READ)
963			++n_read;
964		if (ev->ev_events & EV_WRITE)
965			++n_write;
966		if (ev->ev_events & EV_CLOSED)
967			++n_close;
968	}
969
970	EVUTIL_ASSERT(n_read == io_info->nread);
971	EVUTIL_ASSERT(n_write == io_info->nwrite);
972	EVUTIL_ASSERT(n_close == io_info->nclose);
973
974	return 0;
975}
976
977/* Helper for evmap_check_integrity_: verify that all of the events pending
978 * on given signal are set up correctly. */
979static int
980evmap_signal_check_integrity_fn(struct event_base *base,
981    int signum, struct evmap_signal *sig_info, void *arg)
982{
983	struct event *ev;
984	/* First, make sure the list itself isn't corrupt. */
985	EVUTIL_ASSERT_LIST_OK(&sig_info->events, event, ev_signal_next);
986
987	LIST_FOREACH(ev, &sig_info->events, ev_io_next) {
988		EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED);
989		EVUTIL_ASSERT(ev->ev_fd == signum);
990		EVUTIL_ASSERT((ev->ev_events & EV_SIGNAL));
991		EVUTIL_ASSERT(!(ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)));
992	}
993	return 0;
994}
995
996void
997evmap_check_integrity_(struct event_base *base)
998{
999	evmap_io_foreach_fd(base, evmap_io_check_integrity_fn, NULL);
1000	evmap_signal_foreach_signal(base, evmap_signal_check_integrity_fn, NULL);
1001
1002	if (base->evsel->add == event_changelist_add_)
1003		event_changelist_assert_ok(base);
1004}
1005
1006/* Helper type for evmap_foreach_event_: Bundles a function to call on every
1007 * event, and the user-provided void* to use as its third argument. */
1008struct evmap_foreach_event_helper {
1009	event_base_foreach_event_cb fn;
1010	void *arg;
1011};
1012
1013/* Helper for evmap_foreach_event_: calls a provided function on every event
1014 * pending on a given fd.  */
1015static int
1016evmap_io_foreach_event_fn(struct event_base *base, evutil_socket_t fd,
1017    struct evmap_io *io_info, void *arg)
1018{
1019	struct evmap_foreach_event_helper *h = arg;
1020	struct event *ev;
1021	int r;
1022	LIST_FOREACH(ev, &io_info->events, ev_io_next) {
1023		if ((r = h->fn(base, ev, h->arg)))
1024			return r;
1025	}
1026	return 0;
1027}
1028
1029/* Helper for evmap_foreach_event_: calls a provided function on every event
1030 * pending on a given signal.  */
1031static int
1032evmap_signal_foreach_event_fn(struct event_base *base, int signum,
1033    struct evmap_signal *sig_info, void *arg)
1034{
1035	struct event *ev;
1036	struct evmap_foreach_event_helper *h = arg;
1037	int r;
1038	LIST_FOREACH(ev, &sig_info->events, ev_signal_next) {
1039		if ((r = h->fn(base, ev, h->arg)))
1040			return r;
1041	}
1042	return 0;
1043}
1044
1045int
1046evmap_foreach_event_(struct event_base *base,
1047    event_base_foreach_event_cb fn, void *arg)
1048{
1049	struct evmap_foreach_event_helper h;
1050	int r;
1051	h.fn = fn;
1052	h.arg = arg;
1053	if ((r = evmap_io_foreach_fd(base, evmap_io_foreach_event_fn, &h)))
1054		return r;
1055	return evmap_signal_foreach_signal(base, evmap_signal_foreach_event_fn, &h);
1056}
1057
1058