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