• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   */
57 struct 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. */
65 struct 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
77 struct 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. */
88 static inline unsigned
hashsocket(struct event_map_entry * e)89 hashsocket(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. */
101 static inline int
eqsocket(struct event_map_entry * e1,struct event_map_entry * e2)102 eqsocket(struct event_map_entry *e1, struct event_map_entry *e2)
103 {
104 	return e1->fd == e2->fd;
105 }
106 
HT_PROTOTYPE(event_io_map,event_map_entry,map_node,hashsocket,eqsocket)107 HT_PROTOTYPE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket)
108 HT_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 
139 void evmap_io_initmap(struct event_io_map *ctx)
140 {
141 	HT_INIT(event_io_map, ctx);
142 }
143 
evmap_io_clear(struct event_io_map * ctx)144 void 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)
185 void
evmap_io_initmap(struct event_io_map * ctx)186 evmap_io_initmap(struct event_io_map* ctx)
187 {
188 	evmap_signal_initmap(ctx);
189 }
190 void
evmap_io_clear(struct event_io_map * ctx)191 evmap_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  */
201 static int
evmap_make_space(struct event_signal_map * map,int slot,int msize)202 evmap_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 
225 void
evmap_signal_initmap(struct event_signal_map * ctx)226 evmap_signal_initmap(struct event_signal_map *ctx)
227 {
228 	ctx->nentries = 0;
229 	ctx->entries = NULL;
230 }
231 
232 void
evmap_signal_clear(struct event_signal_map * ctx)233 evmap_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 */
251 static void
evmap_io_init(struct evmap_io * entry)252 evmap_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. */
262 int
evmap_io_add(struct event_base * base,evutil_socket_t fd,struct event * ev)263 evmap_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. */
335 int
evmap_io_del(struct event_base * base,evutil_socket_t fd,struct event * ev)336 evmap_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 
389 void
evmap_io_active(struct event_base * base,evutil_socket_t fd,short events)390 evmap_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 
410 static void
evmap_signal_init(struct evmap_signal * entry)411 evmap_signal_init(struct evmap_signal *entry)
412 {
413 	TAILQ_INIT(&entry->events);
414 }
415 
416 
417 int
evmap_signal_add(struct event_base * base,int sig,struct event * ev)418 evmap_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 
443 int
evmap_signal_del(struct event_base * base,int sig,struct event * ev)444 evmap_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 
465 void
evmap_signal_active(struct event_base * base,evutil_socket_t sig,int ncalls)466 evmap_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 
479 void *
evmap_io_get_fdinfo(struct event_io_map * map,evutil_socket_t fd)480 evmap_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  */
493 struct 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 
498 void
event_changelist_init(struct event_changelist * changelist)499 event_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. */
507 static inline struct event_changelist_fdinfo *
event_change_get_fdinfo(struct event_base * base,const struct event_change * change)508 event_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. */
526 static void
event_changelist_check(struct event_base * base)527 event_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 
559 void
event_changelist_remove_all(struct event_changelist * changelist,struct event_base * base)560 event_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 
580 void
event_changelist_freemem(struct event_changelist * changelist)581 event_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. */
589 static int
event_changelist_grow(struct event_changelist * changelist)590 event_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  */
615 static struct event_change *
event_changelist_get_or_construct(struct event_changelist * changelist,evutil_socket_t fd,short old_events,struct event_changelist_fdinfo * fdinfo)616 event_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 
646 int
event_changelist_add(struct event_base * base,evutil_socket_t fd,short old,short events,void * p)647 event_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 
677 int
event_changelist_del(struct event_base * base,evutil_socket_t fd,short old,short events,void * p)678 event_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 
728 void
evmap_check_integrity(struct event_base * base)729 evmap_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