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