michael@0: /* michael@0: * Copyright 2000-2009 Niels Provos michael@0: * Copyright 2009-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: #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 "event2/event.h" michael@0: #include "event2/event_struct.h" michael@0: #include "event2/thread.h" 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 "evthread-internal.h" michael@0: michael@0: struct devpollop { michael@0: struct pollfd *events; michael@0: int nevents; michael@0: int dpfd; michael@0: struct pollfd *changes; michael@0: int nchanges; michael@0: }; michael@0: michael@0: static void *devpoll_init(struct event_base *); michael@0: static int devpoll_add(struct event_base *, int fd, short old, short events, void *); michael@0: static int devpoll_del(struct event_base *, int fd, short old, short events, void *); michael@0: static int devpoll_dispatch(struct event_base *, struct timeval *); michael@0: static void devpoll_dealloc(struct event_base *); michael@0: michael@0: const struct eventop devpollops = { michael@0: "devpoll", michael@0: devpoll_init, michael@0: devpoll_add, michael@0: devpoll_del, michael@0: devpoll_dispatch, michael@0: devpoll_dealloc, michael@0: 1, /* need reinit */ michael@0: EV_FEATURE_FDS|EV_FEATURE_O1, michael@0: 0 michael@0: }; michael@0: michael@0: #define NEVENT 32000 michael@0: michael@0: static int michael@0: devpoll_commit(struct devpollop *devpollop) michael@0: { michael@0: /* michael@0: * Due to a bug in Solaris, we have to use pwrite with an offset of 0. michael@0: * Write is limited to 2GB of data, until it will fail. michael@0: */ michael@0: if (pwrite(devpollop->dpfd, devpollop->changes, michael@0: sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) michael@0: return (-1); michael@0: michael@0: devpollop->nchanges = 0; michael@0: return (0); michael@0: } michael@0: michael@0: static int michael@0: devpoll_queue(struct devpollop *devpollop, int fd, int events) { michael@0: struct pollfd *pfd; michael@0: michael@0: if (devpollop->nchanges >= devpollop->nevents) { michael@0: /* michael@0: * Change buffer is full, must commit it to /dev/poll before michael@0: * adding more michael@0: */ michael@0: if (devpoll_commit(devpollop) != 0) michael@0: return (-1); michael@0: } michael@0: michael@0: pfd = &devpollop->changes[devpollop->nchanges++]; michael@0: pfd->fd = fd; michael@0: pfd->events = events; michael@0: pfd->revents = 0; michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: static void * michael@0: devpoll_init(struct event_base *base) michael@0: { michael@0: int dpfd, nfiles = NEVENT; michael@0: struct rlimit rl; michael@0: struct devpollop *devpollop; michael@0: michael@0: if (!(devpollop = mm_calloc(1, sizeof(struct devpollop)))) michael@0: return (NULL); michael@0: michael@0: if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && michael@0: rl.rlim_cur != RLIM_INFINITY) michael@0: nfiles = rl.rlim_cur; michael@0: michael@0: /* Initialize the kernel queue */ michael@0: if ((dpfd = evutil_open_closeonexec("/dev/poll", O_RDWR, 0)) == -1) { michael@0: event_warn("open: /dev/poll"); michael@0: mm_free(devpollop); michael@0: return (NULL); michael@0: } michael@0: michael@0: devpollop->dpfd = dpfd; michael@0: michael@0: /* Initialize fields */ michael@0: /* FIXME: allocating 'nfiles' worth of space here can be michael@0: * expensive and unnecessary. See how epoll.c does it instead. */ michael@0: devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd)); michael@0: if (devpollop->events == NULL) { michael@0: mm_free(devpollop); michael@0: close(dpfd); michael@0: return (NULL); michael@0: } michael@0: devpollop->nevents = nfiles; michael@0: michael@0: devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd)); michael@0: if (devpollop->changes == NULL) { michael@0: mm_free(devpollop->events); michael@0: mm_free(devpollop); michael@0: close(dpfd); michael@0: return (NULL); michael@0: } michael@0: michael@0: evsig_init(base); michael@0: michael@0: return (devpollop); michael@0: } michael@0: michael@0: static int michael@0: devpoll_dispatch(struct event_base *base, struct timeval *tv) michael@0: { michael@0: struct devpollop *devpollop = base->evbase; michael@0: struct pollfd *events = devpollop->events; michael@0: struct dvpoll dvp; michael@0: int i, res, timeout = -1; michael@0: michael@0: if (devpollop->nchanges) michael@0: devpoll_commit(devpollop); michael@0: michael@0: if (tv != NULL) michael@0: timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; michael@0: michael@0: dvp.dp_fds = devpollop->events; michael@0: dvp.dp_nfds = devpollop->nevents; michael@0: dvp.dp_timeout = timeout; michael@0: michael@0: EVBASE_RELEASE_LOCK(base, th_base_lock); michael@0: michael@0: res = ioctl(devpollop->dpfd, DP_POLL, &dvp); 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("ioctl: DP_POLL"); michael@0: return (-1); michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: event_debug(("%s: devpoll_wait reports %d", __func__, res)); michael@0: michael@0: for (i = 0; i < res; i++) { michael@0: int which = 0; michael@0: int what = events[i].revents; michael@0: michael@0: if (what & POLLHUP) michael@0: what |= POLLIN | POLLOUT; michael@0: else if (what & POLLERR) michael@0: what |= POLLIN | POLLOUT; michael@0: michael@0: if (what & POLLIN) michael@0: which |= EV_READ; michael@0: if (what & POLLOUT) michael@0: which |= EV_WRITE; michael@0: michael@0: if (!which) michael@0: continue; michael@0: michael@0: /* XXX(niels): not sure if this works for devpoll */ michael@0: evmap_io_active(base, events[i].fd, which); michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: michael@0: static int michael@0: devpoll_add(struct event_base *base, int fd, short old, short events, void *p) michael@0: { michael@0: struct devpollop *devpollop = base->evbase; michael@0: int res; michael@0: (void)p; michael@0: michael@0: /* michael@0: * It's not necessary to OR the existing read/write events that we michael@0: * are currently interested in with the new event we are adding. michael@0: * The /dev/poll driver ORs any new events with the existing events michael@0: * that it has cached for the fd. michael@0: */ michael@0: michael@0: res = 0; michael@0: if (events & EV_READ) michael@0: res |= POLLIN; michael@0: if (events & EV_WRITE) michael@0: res |= POLLOUT; michael@0: michael@0: if (devpoll_queue(devpollop, fd, res) != 0) michael@0: return (-1); michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: static int michael@0: devpoll_del(struct event_base *base, int fd, short old, short events, void *p) michael@0: { michael@0: struct devpollop *devpollop = base->evbase; michael@0: int res; michael@0: (void)p; michael@0: michael@0: res = 0; michael@0: if (events & EV_READ) michael@0: res |= POLLIN; michael@0: if (events & EV_WRITE) michael@0: res |= POLLOUT; michael@0: michael@0: /* michael@0: * The only way to remove an fd from the /dev/poll monitored set is michael@0: * to use POLLREMOVE by itself. This removes ALL events for the fd michael@0: * provided so if we care about two events and are only removing one michael@0: * we must re-add the other event after POLLREMOVE. michael@0: */ michael@0: michael@0: if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) michael@0: return (-1); michael@0: michael@0: if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { michael@0: /* michael@0: * We're not deleting all events, so we must resubmit the michael@0: * event that we are still interested in if one exists. michael@0: */ michael@0: michael@0: if ((res & POLLIN) && (old & EV_WRITE)) { michael@0: /* Deleting read, still care about write */ michael@0: devpoll_queue(devpollop, fd, POLLOUT); michael@0: } else if ((res & POLLOUT) && (old & EV_READ)) { michael@0: /* Deleting write, still care about read */ michael@0: devpoll_queue(devpollop, fd, POLLIN); michael@0: } michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: static void michael@0: devpoll_dealloc(struct event_base *base) michael@0: { michael@0: struct devpollop *devpollop = base->evbase; michael@0: michael@0: evsig_dealloc(base); michael@0: if (devpollop->events) michael@0: mm_free(devpollop->events); michael@0: if (devpollop->changes) michael@0: mm_free(devpollop->changes); michael@0: if (devpollop->dpfd >= 0) michael@0: close(devpollop->dpfd); michael@0: michael@0: memset(devpollop, 0, sizeof(struct devpollop)); michael@0: mm_free(devpollop); michael@0: }