michael@0: /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ michael@0: michael@0: /* michael@0: * Copyright 2000-2007 Niels Provos michael@0: * Copyright 2007-2012 Niels Provos and Nick Mathewson michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. The name of the author may not be used to endorse or promote products michael@0: * derived from this software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR michael@0: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES michael@0: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. michael@0: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, michael@0: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT michael@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF michael@0: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: #include "event2/event-config.h" michael@0: michael@0: #include michael@0: #ifdef _EVENT_HAVE_SYS_TIME_H michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "event-internal.h" michael@0: #include "evsignal-internal.h" michael@0: #include "log-internal.h" michael@0: #include "evmap-internal.h" michael@0: #include "event2/thread.h" michael@0: #include "evthread-internal.h" michael@0: michael@0: struct pollidx { michael@0: int idxplus1; michael@0: }; michael@0: michael@0: struct pollop { michael@0: int event_count; /* Highest number alloc */ michael@0: int nfds; /* Highest number used */ michael@0: int realloc_copy; /* True iff we must realloc michael@0: * event_set_copy */ michael@0: struct pollfd *event_set; michael@0: struct pollfd *event_set_copy; michael@0: }; michael@0: michael@0: static void *poll_init(struct event_base *); michael@0: static int poll_add(struct event_base *, int, short old, short events, void *_idx); michael@0: static int poll_del(struct event_base *, int, short old, short events, void *_idx); michael@0: static int poll_dispatch(struct event_base *, struct timeval *); michael@0: static void poll_dealloc(struct event_base *); michael@0: michael@0: const struct eventop pollops = { michael@0: "poll", michael@0: poll_init, michael@0: poll_add, michael@0: poll_del, michael@0: poll_dispatch, michael@0: poll_dealloc, michael@0: 0, /* doesn't need_reinit */ michael@0: EV_FEATURE_FDS, michael@0: sizeof(struct pollidx), michael@0: }; michael@0: michael@0: static void * michael@0: poll_init(struct event_base *base) michael@0: { michael@0: struct pollop *pollop; michael@0: michael@0: if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) michael@0: return (NULL); michael@0: michael@0: evsig_init(base); michael@0: michael@0: return (pollop); michael@0: } michael@0: michael@0: #ifdef CHECK_INVARIANTS michael@0: static void michael@0: poll_check_ok(struct pollop *pop) michael@0: { michael@0: int i, idx; michael@0: struct event *ev; michael@0: michael@0: for (i = 0; i < pop->fd_count; ++i) { michael@0: idx = pop->idxplus1_by_fd[i]-1; michael@0: if (idx < 0) michael@0: continue; michael@0: EVUTIL_ASSERT(pop->event_set[idx].fd == i); michael@0: } michael@0: for (i = 0; i < pop->nfds; ++i) { michael@0: struct pollfd *pfd = &pop->event_set[i]; michael@0: EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); michael@0: } michael@0: } michael@0: #else michael@0: #define poll_check_ok(pop) michael@0: #endif michael@0: michael@0: static int michael@0: poll_dispatch(struct event_base *base, struct timeval *tv) michael@0: { michael@0: int res, i, j, nfds; michael@0: long msec = -1; michael@0: struct pollop *pop = base->evbase; michael@0: struct pollfd *event_set; michael@0: michael@0: poll_check_ok(pop); michael@0: michael@0: nfds = pop->nfds; michael@0: michael@0: #ifndef _EVENT_DISABLE_THREAD_SUPPORT michael@0: if (base->th_base_lock) { michael@0: /* If we're using this backend in a multithreaded setting, michael@0: * then we need to work on a copy of event_set, so that we can michael@0: * let other threads modify the main event_set while we're michael@0: * polling. If we're not multithreaded, then we'll skip the michael@0: * copy step here to save memory and time. */ michael@0: if (pop->realloc_copy) { michael@0: struct pollfd *tmp = mm_realloc(pop->event_set_copy, michael@0: pop->event_count * sizeof(struct pollfd)); michael@0: if (tmp == NULL) { michael@0: event_warn("realloc"); michael@0: return -1; michael@0: } michael@0: pop->event_set_copy = tmp; michael@0: pop->realloc_copy = 0; michael@0: } michael@0: memcpy(pop->event_set_copy, pop->event_set, michael@0: sizeof(struct pollfd)*nfds); michael@0: event_set = pop->event_set_copy; michael@0: } else { michael@0: event_set = pop->event_set; michael@0: } michael@0: #else michael@0: event_set = pop->event_set; michael@0: #endif michael@0: michael@0: if (tv != NULL) { michael@0: msec = evutil_tv_to_msec(tv); michael@0: if (msec < 0 || msec > INT_MAX) michael@0: msec = INT_MAX; michael@0: } michael@0: michael@0: EVBASE_RELEASE_LOCK(base, th_base_lock); michael@0: michael@0: res = poll(event_set, nfds, msec); michael@0: michael@0: EVBASE_ACQUIRE_LOCK(base, th_base_lock); michael@0: michael@0: if (res == -1) { michael@0: if (errno != EINTR) { michael@0: event_warn("poll"); michael@0: return (-1); michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: event_debug(("%s: poll reports %d", __func__, res)); michael@0: michael@0: if (res == 0 || nfds == 0) michael@0: return (0); michael@0: michael@0: i = random() % nfds; michael@0: for (j = 0; j < nfds; j++) { michael@0: int what; michael@0: if (++i == nfds) michael@0: i = 0; michael@0: what = event_set[i].revents; michael@0: if (!what) michael@0: continue; michael@0: michael@0: res = 0; michael@0: michael@0: /* If the file gets closed notify */ michael@0: if (what & (POLLHUP|POLLERR)) michael@0: what |= POLLIN|POLLOUT; michael@0: if (what & POLLIN) michael@0: res |= EV_READ; michael@0: if (what & POLLOUT) michael@0: res |= EV_WRITE; michael@0: if (res == 0) michael@0: continue; michael@0: michael@0: evmap_io_active(base, event_set[i].fd, res); michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: static int michael@0: poll_add(struct event_base *base, int fd, short old, short events, void *_idx) michael@0: { michael@0: struct pollop *pop = base->evbase; michael@0: struct pollfd *pfd = NULL; michael@0: struct pollidx *idx = _idx; michael@0: int i; michael@0: michael@0: EVUTIL_ASSERT((events & EV_SIGNAL) == 0); michael@0: if (!(events & (EV_READ|EV_WRITE))) michael@0: return (0); michael@0: michael@0: poll_check_ok(pop); michael@0: if (pop->nfds + 1 >= pop->event_count) { michael@0: struct pollfd *tmp_event_set; michael@0: int tmp_event_count; michael@0: michael@0: if (pop->event_count < 32) michael@0: tmp_event_count = 32; michael@0: else michael@0: tmp_event_count = pop->event_count * 2; michael@0: michael@0: /* We need more file descriptors */ michael@0: tmp_event_set = mm_realloc(pop->event_set, michael@0: tmp_event_count * sizeof(struct pollfd)); michael@0: if (tmp_event_set == NULL) { michael@0: event_warn("realloc"); michael@0: return (-1); michael@0: } michael@0: pop->event_set = tmp_event_set; michael@0: michael@0: pop->event_count = tmp_event_count; michael@0: pop->realloc_copy = 1; michael@0: } michael@0: michael@0: i = idx->idxplus1 - 1; michael@0: michael@0: if (i >= 0) { michael@0: pfd = &pop->event_set[i]; michael@0: } else { michael@0: i = pop->nfds++; michael@0: pfd = &pop->event_set[i]; michael@0: pfd->events = 0; michael@0: pfd->fd = fd; michael@0: idx->idxplus1 = i + 1; michael@0: } michael@0: michael@0: pfd->revents = 0; michael@0: if (events & EV_WRITE) michael@0: pfd->events |= POLLOUT; michael@0: if (events & EV_READ) michael@0: pfd->events |= POLLIN; michael@0: poll_check_ok(pop); michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: /* michael@0: * Nothing to be done here. michael@0: */ michael@0: michael@0: static int michael@0: poll_del(struct event_base *base, int fd, short old, short events, void *_idx) michael@0: { michael@0: struct pollop *pop = base->evbase; michael@0: struct pollfd *pfd = NULL; michael@0: struct pollidx *idx = _idx; michael@0: int i; michael@0: michael@0: EVUTIL_ASSERT((events & EV_SIGNAL) == 0); michael@0: if (!(events & (EV_READ|EV_WRITE))) michael@0: return (0); michael@0: michael@0: poll_check_ok(pop); michael@0: i = idx->idxplus1 - 1; michael@0: if (i < 0) michael@0: return (-1); michael@0: michael@0: /* Do we still want to read or write? */ michael@0: pfd = &pop->event_set[i]; michael@0: if (events & EV_READ) michael@0: pfd->events &= ~POLLIN; michael@0: if (events & EV_WRITE) michael@0: pfd->events &= ~POLLOUT; michael@0: poll_check_ok(pop); michael@0: if (pfd->events) michael@0: /* Another event cares about that fd. */ michael@0: return (0); michael@0: michael@0: /* Okay, so we aren't interested in that fd anymore. */ michael@0: idx->idxplus1 = 0; michael@0: michael@0: --pop->nfds; michael@0: if (i != pop->nfds) { michael@0: /* michael@0: * Shift the last pollfd down into the now-unoccupied michael@0: * position. michael@0: */ michael@0: memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], michael@0: sizeof(struct pollfd)); michael@0: idx = evmap_io_get_fdinfo(&base->io, pop->event_set[i].fd); michael@0: EVUTIL_ASSERT(idx); michael@0: EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); michael@0: idx->idxplus1 = i + 1; michael@0: } michael@0: michael@0: poll_check_ok(pop); michael@0: return (0); michael@0: } michael@0: michael@0: static void michael@0: poll_dealloc(struct event_base *base) michael@0: { michael@0: struct pollop *pop = base->evbase; michael@0: michael@0: evsig_dealloc(base); michael@0: if (pop->event_set) michael@0: mm_free(pop->event_set); michael@0: if (pop->event_set_copy) michael@0: mm_free(pop->event_set_copy); michael@0: michael@0: memset(pop, 0, sizeof(struct pollop)); michael@0: mm_free(pop); michael@0: }