From 3e41f17afacd87c3bf0a48adc395c3f7e4b7d8bf Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Fri, 7 Mar 2003 23:20:36 +0000 Subject: [PATCH] support for Linux eventpoll mechanism svn:r42 --- epoll.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++ epoll_sub.c | 26 ++++ event.c | 6 + 3 files changed, 366 insertions(+) create mode 100644 epoll.c create mode 100644 epoll_sub.c diff --git a/epoll.c b/epoll.c new file mode 100644 index 00000000..95dab907 --- /dev/null +++ b/epoll.c @@ -0,0 +1,334 @@ +/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_LOG +#include "log.h" +#else +#define LOG_DBG(x) +#define log_error warn +#endif + +#include "event.h" + +extern struct event_list eventqueue; + +extern volatile sig_atomic_t evsignal_caught; + +/* due to limitations in the epoll interface, we need to keep track of + * all file descriptors outself. + */ +struct evepoll { + struct event *evread; + struct event *evwrite; +}; + +struct epollop { + struct evepoll *fds; + int nfds; + struct epoll_event *events; + int nevents; + int epfd; + sigset_t evsigmask; +} epollop; + +void evsignal_init(sigset_t *); +void evsignal_process(void); +int evsignal_recalc(sigset_t *); +int evsignal_deliver(void); +int evsignal_add(sigset_t *, struct event *); +int evsignal_del(sigset_t *, struct event *); + +void *epoll_init (void); +int epoll_add (void *, struct event *); +int epoll_del (void *, struct event *); +int epoll_recalc (void *, int); +int epoll_dispatch (void *, struct timeval *); + +struct eventop epollops = { + "epoll", + epoll_init, + epoll_add, + epoll_del, + epoll_recalc, + epoll_dispatch +}; + +#define NEVENT 64 + +void * +epoll_init(void) +{ + int epfd; + + /* Disable epollueue when this environment variable is set */ + if (getenv("EVENT_NOEPOLL")) + return (NULL); + + memset(&epollop, 0, sizeof(epollop)); + + /* Initalize the kernel queue */ + + if ((epfd = epoll_create(NEVENT)) == -1) { + log_error("epoll_create"); + return (NULL); + } + + epollop.epfd = epfd; + + /* Initalize fields */ + epollop.events = malloc(NEVENT * sizeof(struct epoll_event)); + if (epollop.events == NULL) + return (NULL); + epollop.nevents = NEVENT; + + epollop.fds = calloc(NEVENT, sizeof(struct evepoll)); + if (epollop.fds == NULL) { + free(epollop.events); + return (NULL); + } + epollop.nfds = NEVENT; + + evsignal_init(&epollop.evsigmask); + + return (&epollop); +} + +int +epoll_recalc(void *arg, int max) +{ + struct epollop *epollop = arg; + + if (max > epollop->nfds) { + struct evepoll *fds; + int nfds; + + nfds = 2*epollop->nfds; + if (nfds < max) + nfds = max; + + fds = realloc(epollop->fds, nfds * sizeof(struct evepoll)); + if (fds == NULL) { + log_error("realloc"); + return (-1); + } + epollop->fds = fds; + memset(fds + epollop->nfds, 0, + (nfds - epollop->nfds) * sizeof(struct evepoll)); + epollop->nfds = nfds; + } + + return (evsignal_recalc(&epollop->evsigmask)); +} + +int +epoll_dispatch(void *arg, struct timeval *tv) +{ + struct epollop *epollop = arg; + struct epoll_event *events = epollop->events; + struct evepoll *evep; + struct event *ev; + int i, res, timeout; + + if (evsignal_deliver() == -1) + return (-1); + + timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000; + res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout); + + if (evsignal_recalc(&epollop->evsigmask) == -1) + return (-1); + + if (res == -1) { + if (errno != EINTR) { + log_error("epoll_wait"); + return (-1); + } + + evsignal_process(); + return (0); + } else if (evsignal_caught) + evsignal_process(); + + LOG_DBG((LOG_MISC, 80, "%s: epoll_wait reports %d", __func__, res)); + + for (i = 0; i < res; i++) { + int which = 0, what; + + evep = (struct evepoll *)events[i].data.ptr; + what = events[i].events; + if (what & EPOLLIN) { + which |= EV_READ; + } + + if (what & EPOLLOUT) { + which |= EV_WRITE; + } + + if (!which) + continue; + + if (which & EV_READ) { + ev = evep->evread; + if (!(ev->ev_events & EV_PERSIST)) + event_del(ev); + event_active(ev, EV_READ, 1); + } + if (which & EV_WRITE) { + ev = evep->evwrite; + if (!(ev->ev_events & EV_PERSIST)) + event_del(ev); + event_active(ev, EV_WRITE, 1); + } + } + + return (0); +} + + +int +epoll_add(void *arg, struct event *ev) +{ + struct epollop *epollop = arg; + struct epoll_event epev; + struct evepoll *evep; + int fd, op, events; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_add(&epollop->evsigmask, ev)); + + fd = ev->ev_fd; + if (fd >= epollop->nfds) { + /* Extent the file descriptor array as necessary */ + if (epoll_recalc(epollop, fd) == -1) + return (-1); + } + evep = &epollop->fds[fd]; + op = EPOLL_CTL_ADD; + events = 0; + if (evep->evread != NULL) { + events |= EPOLLIN; + op = EPOLL_CTL_MOD; + } + if (evep->evwrite != NULL) { + events |= EPOLLOUT; + op = EPOLL_CTL_MOD; + } + + if (ev->ev_events & EV_READ) + events |= EPOLLIN; + if (ev->ev_events & EV_WRITE) + events |= EPOLLOUT; + + epev.data.ptr = evep; + epev.events = events; + if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1) + return (-1); + + /* Update events responsible */ + if (ev->ev_events & EV_READ) + evep->evread = ev; + if (ev->ev_events & EV_WRITE) + evep->evwrite = ev; + + return (0); +} + +int +epoll_del(void *arg, struct event *ev) +{ + struct epollop *epollop = arg; + struct epoll_event epev; + struct evepoll *evep; + int fd, events, op; + int needwritedelete = 1, needreaddelete = 1; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(&epollop->evsigmask, ev)); + + fd = ev->ev_fd; + if (fd >= epollop->nfds) + return (0); + evep = &epollop->fds[fd]; + + op = EPOLL_CTL_DEL; + events = 0; + + if (ev->ev_events & EV_READ) + events |= EPOLLIN; + if (ev->ev_events & EV_WRITE) + events |= EPOLLOUT; + + if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) { + if ((events & EPOLLIN) && evep->evwrite != NULL) { + needwritedelete = 0; + events = EPOLLOUT; + op = EPOLL_CTL_MOD; + } else if ((events & EPOLLOUT) && evep->evread != NULL) { + needreaddelete = 0; + events = EPOLLIN; + op = EPOLL_CTL_MOD; + } + } + + epev.events = events; + epev.data.ptr = evep; + + if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1) + return (-1); + + if (needreaddelete) + evep->evread = NULL; + if (needwritedelete) + evep->evwrite = NULL; + + return (0); +} diff --git a/epoll_sub.c b/epoll_sub.c new file mode 100644 index 00000000..598681e7 --- /dev/null +++ b/epoll_sub.c @@ -0,0 +1,26 @@ +#include + +#include +#include +#include +#include +#include + +int +epoll_create(int size) +{ + return (syscall(__NR_epoll_create, size)); +} + +int +epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + + return (syscall(__NR_epoll_ctl, epfd, op, fd, event)); +} + +int +epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) +{ + return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout)); +} diff --git a/event.c b/event.c index 48073c79..1bf545c4 100644 --- a/event.c +++ b/event.c @@ -64,6 +64,9 @@ extern struct eventop selectops; #ifdef HAVE_POLL extern struct eventop pollops; #endif +#ifdef HAVE_EPOLL +extern struct eventop epollops; +#endif #ifdef HAVE_WORKING_KQUEUE extern struct eventop kqops; #endif @@ -73,6 +76,9 @@ struct eventop *eventops[] = { #ifdef HAVE_WORKING_KQUEUE &kqops, #endif +#ifdef HAVE_EPOLL + &epollops, +#endif #ifdef HAVE_POLL &pollops, #endif -- 2.40.0