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 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 102 eqsocket(struct event_map_entry *e1, struct event_map_entry *e2) 103 { 104 return e1->fd == e2->fd; 105 } 106 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 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 186 evmap_io_initmap(struct event_io_map* ctx) 187 { 188 evmap_signal_initmap(ctx); 189 } 190 void 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 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 226 evmap_signal_initmap(struct event_signal_map *ctx) 227 { 228 ctx->nentries = 0; 229 ctx->entries = NULL; 230 } 231 232 void 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 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 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 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 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 411 evmap_signal_init(struct evmap_signal *entry) 412 { 413 TAILQ_INIT(&entry->events); 414 } 415 416 417 int 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 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 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 * 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 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 * 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 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 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 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 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 * 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 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 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 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