]> granicus.if.org Git - libevent/commitdiff
1.2-rc1; Solaris' event port support from Dave Pacheco
authorNiels Provos <provos@gmail.com>
Sat, 15 Jul 2006 02:55:57 +0000 (02:55 +0000)
committerNiels Provos <provos@gmail.com>
Sat, 15 Jul 2006 02:55:57 +0000 (02:55 +0000)
svn:r216

Makefile.am
configure.in
event.c
evport.c [new file with mode: 0644]

index 625fb5d7e683d7003f49fc68831fa3560cb22eac..1951e810564942ba48e9aa30dd29c83fc26f7525 100644 (file)
@@ -6,7 +6,7 @@ bin_SCRIPTS = event_rpcgen.py
 
 EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \
        kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
-       devpoll.c event_rpcgen.py \
+       evport.c devpoll.c event_rpcgen.py \
        sample/Makefile.am sample/Makefile.in sample/event-test.c \
        sample/signal-test.c sample/time-test.c \
        test/Makefile.am test/Makefile.in test/bench.c test/regress.c \
index 42e17a731ee815ed23fc5a7c4b72dce758936c7a..45ec39e6eff5bb46f1bd6ab4d78371907d1a2b43 100644 (file)
@@ -2,7 +2,7 @@ dnl configure.in for libevent
 dnl Dug Song <dugsong@monkey.org>
 AC_INIT(event.c)
 
-AM_INIT_AUTOMAKE(libevent,1.2)
+AM_INIT_AUTOMAKE(libevent,1.2-rc1)
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
@@ -39,7 +39,7 @@ AC_CHECK_LIB(socket, socket)
 
 dnl Checks for header files.
 AC_HEADER_STDC
-AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/ioctl.h sys/devpoll.h)
+AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/ioctl.h sys/devpoll.h port.h)
 if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
        AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
        AC_EGREP_CPP(yes,
@@ -302,6 +302,14 @@ main(int argc, char **argv)
        fi
 fi
 
+haveeventports=no
+AC_CHECK_FUNCS(port_create, [haveeventports=yes], )
+if test "x$haveeventports" = "xyes" ; then
+       AC_DEFINE(HAVE_EVENT_PORTS, 1,
+               [Define if your system supports event ports])
+       AC_LIBOBJ(evport)
+       needsignal=yes
+fi
 if test "x$needsignal" = "xyes" ; then
        AC_LIBOBJ(signal)
 fi
@@ -339,4 +347,5 @@ AC_TRY_COMPILE([],
    AC_DEFINE(__func__, __FILE__,
          [Define to appropriate substitue if compiler doesnt have __func__])))
 
+
 AC_OUTPUT(Makefile test/Makefile sample/Makefile)
diff --git a/event.c b/event.c
index 8621f6c27d29684aaa98601bb426681dd5278467..c7ece90f200235b88348ec02fc32dbd3dc42b73c 100644 (file)
--- a/event.c
+++ b/event.c
@@ -56,6 +56,9 @@
 #include "event-internal.h"
 #include "log.h"
 
+#ifdef HAVE_EVENT_PORTS
+extern const struct eventop evportops;
+#endif
 #ifdef HAVE_SELECT
 extern const struct eventop selectops;
 #endif
@@ -80,6 +83,9 @@ extern const struct eventop win32ops;
 
 /* In order of preference */
 const struct eventop *eventops[] = {
+#ifdef HAVE_EVENT_PORTS
+       &evportops,
+#endif
 #ifdef HAVE_WORKING_KQUEUE
        &kqops,
 #endif
diff --git a/evport.c b/evport.c
new file mode 100644 (file)
index 0000000..b35229f
--- /dev/null
+++ b/evport.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 2006 Sun Microsystems.
+ * All rights reserved.
+ * Submitted by David Pacheco (dp.spambait@gmail.com)
+ *
+ * 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. 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.
+ */
+
+/*
+ * evport.c: event backend using Solaris 10 event ports. See port_create(3C).
+ * This implementation is loosely modeled after the one used for select(2) (in
+ * select.c).
+ *
+ * The outstanding events are tracked in a data structure called evport_data.
+ * Each entry in the ed_fds array corresponds to a file descriptor, and contains
+ * pointers to the read and write events that correspond to that fd. (That is,
+ * when the file is readable, the "read" event should handle it, etc.)
+ *
+ * evport_add and evport_del update this data structure. evport_dispatch uses it
+ * to determine where to callback when an event occurs (which it gets from
+ * port_getn). 
+ *
+ * Helper functions are used: grow() grows the file descriptor array as
+ * necessary when large fd's come in. reassociate() takes care of maintaining
+ * the proper file-descriptor/event-port associations.
+ *
+ * As in the select(2) implementation, signals are handled by evsignal, and
+ * evport_recalc does almost nothing.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <sys/_time.h>
+#endif
+#include <assert.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <errno.h>
+#include <poll.h>
+#include <port.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef CHECK_INVARIANTS
+#include <assert.h>
+#endif
+
+#include "event.h"
+#include "event-internal.h"
+#include "log.h"
+#include "evsignal.h"
+
+extern volatile sig_atomic_t evsignal_caught;
+
+
+/*
+ * Default value for ed_nevents, which is the maximum file descriptor number we
+ * can handle. If an event comes in for a file descriptor F > nevents, we will
+ * grow the array of file descriptors, doubling its size.
+ */
+#define DEFAULT_NFDS   16
+
+
+/*
+ * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
+ * any particular call. You can speed things up by increasing this, but it will
+ * (obviously) require more memory.
+ */
+#define EVENTS_PER_GETN 8
+
+/*
+ * Per-file-descriptor information about what events we're subscribed to. These
+ * fields are NULL if no event is subscribed to either of them.
+ */
+
+struct fd_info {
+       struct event* fdi_revt; /* the event responsible for the "read"  */
+       struct event* fdi_wevt; /* the event responsible for the "write" */
+};
+
+#define FDI_HAS_READ(fdi)  ((fdi)->fdi_revt != NULL)
+#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL)
+#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
+#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
+    (FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
+
+struct evport_data {
+       int             ed_port;        /* event port for system events  */
+       sigset_t        ed_sigmask;     /* for evsignal                  */
+       int             ed_nevents;     /* number of allocated fdi's     */
+       struct fd_info *ed_fds;         /* allocated fdi table           */
+       /* fdi's that we need to reassoc */
+       struct fd_info *ed_pending[EVENTS_PER_GETN];
+};
+
+static void*   evport_init     (void);
+static int     evport_add      (void *, struct event *);
+static int     evport_del      (void *, struct event *);
+static int     evport_recalc   (struct event_base *, void *, int);
+static int     evport_dispatch (struct event_base *, void *, struct timeval *);
+
+const struct eventop evportops = {
+       "event ports",
+       evport_init,
+       evport_add,
+       evport_del,
+       evport_recalc,
+       evport_dispatch
+};
+
+/*
+ * Initialize the event port implementation.
+ */
+
+static void*
+evport_init(void)
+{
+       struct evport_data *evpd;
+       /*
+        * Disable event ports when this environment variable is set 
+        */
+       if (getenv("EVENT_NOEVPORT"))
+               return (NULL);
+
+       if (!(evpd = calloc(1, sizeof(struct evport_data))))
+               return (NULL);
+
+       if ((evpd->ed_port = port_create()) == -1) {
+               free(evpd);
+               return (NULL);
+       }
+
+       /*
+        * Initialize file descriptor structure
+        */
+       evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info));
+       if (evpd->ed_fds == NULL) {
+               close(evpd->ed_port);
+               free(evpd);
+               return (NULL);
+       }
+       evpd->ed_nevents = DEFAULT_NFDS;
+       memset(&evpd->ed_pending, 0, EVENTS_PER_GETN * sizeof(struct fd_info*));
+
+       evsignal_init(&evpd->ed_sigmask);
+
+       return (evpd);
+}
+
+#ifdef CHECK_INVARIANTS
+/*
+ * Checks some basic properties about the evport_data structure. Because it
+ * checks all file descriptors, this function can be expensive when the maximum
+ * file descriptor ever used is rather large.
+ */
+
+static void
+check_evportop(struct evport_data *evpd)
+{
+       assert(evpd);
+       assert(evpd->ed_nevents > 0);
+       assert(evpd->ed_port > 0);
+       assert(evpd->ed_fds > 0);
+
+       /*
+        * Verify the integrity of the fd_info struct as well as the events to
+        * which it points (at least, that they're valid references and correct
+        * for their position in the structure).
+        */
+       int i;
+       for (i = 0; i < evpd->ed_nevents; ++i) {
+               struct event    *ev;
+               struct fd_info  *fdi;
+
+               fdi = &evpd->ed_fds[i];
+               if ((ev = fdi->fdi_revt) != NULL) {
+                       assert(ev->ev_fd == i);
+               }
+               if ((ev = fdi->fdi_wevt) != NULL) {
+                       assert(ev->ev_fd == i);
+               }
+       }
+}
+
+/*
+ * Verifies very basic integrity of a given port_event.
+ */
+static void
+check_event(port_event_t* pevt)
+{
+       /*
+        * We've only registered for PORT_SOURCE_FD events. The only
+        * other thing we can legitimately receive is PORT_SOURCE_ALERT,
+        * but since we're not using port_alert either, we can assume
+        * PORT_SOURCE_FD.
+        */
+       assert(pevt->portev_source == PORT_SOURCE_FD);
+       assert(pevt->portev_user == NULL);
+}
+
+#else
+#define check_evportop(epop)
+#define check_event(pevt)
+#endif /* CHECK_INVARIANTS */
+
+/*
+ * Doubles the size of the allocated file descriptor array.
+ */
+static int
+grow(struct evport_data *epdp, int factor)
+{
+       struct fd_info *tmp;
+       int oldsize = epdp->ed_nevents;
+       int newsize = factor * oldsize;
+       assert(factor > 1);
+
+       check_evportop(epdp);
+
+       tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
+       if (NULL == tmp)
+               return -1;
+       epdp->ed_fds = tmp;
+       memset((char*) (epdp->ed_fds + oldsize), 0, 
+           (newsize - oldsize)*sizeof(struct fd_info));
+       epdp->ed_nevents = newsize;
+
+       check_evportop(epdp);
+
+       return 0;
+}
+
+
+/*
+ * (Re)associates the given file descriptor with the event port. The OS events
+ * are specified (implicitly) from the fd_info struct.
+ */
+static int
+reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
+{
+       int sysevents = FDI_TO_SYSEVENTS(fdip);
+
+       if (sysevents != 0) {
+               if ((-1 == port_associate(epdp->ed_port, PORT_SOURCE_FD,
+                   fd, sysevents, NULL))) {
+                       perror("port_associate");
+                       return (-1);
+               }
+       } else {
+               if (-1 == port_dissociate(epdp->ed_port, PORT_SOURCE_FD, fd)) {
+                       perror("port_dissociate");
+                       return (-1);
+               }
+       }
+
+       check_evportop(epdp);
+
+       return (0);
+}
+
+/*
+ * Main event loop - polls port_getn for some number of events, and processes
+ * them.
+ */
+
+static int
+evport_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+       int i, res;
+       struct evport_data *epdp = arg;
+       port_event_t pevtlist[EVENTS_PER_GETN];
+
+       /*
+        * port_getn will block until it has at least nevents events. It will
+        * also return how many it's given us (which may be more than we asked
+        * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
+        * nevents.
+        */
+       int nevents = 1;
+
+       /*
+        * We have to convert a struct timeval to a struct timespec
+        * (only difference is nanoseconds vs. microseconds)
+        */
+       struct timespec ts = {tv->tv_sec, tv->tv_usec * 1000};
+
+       /*
+        * Before doing anything else, we need to reassociate the events we hit
+        * last time which need reassociation. See comment at the end of the
+        * loop below.
+        */
+       for (i = 0; i < EVENTS_PER_GETN; ++i) {
+               struct fd_info *fdi = epdp->ed_pending[i];
+
+               if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
+                       int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd : 
+                           fdi->fdi_wevt->ev_fd;
+                       reassociate(epdp, fdi, fd);
+                       epdp->ed_pending[i] = NULL;
+               }
+       }
+
+       
+
+       if (evsignal_deliver(&epdp->ed_sigmask) == -1)
+               return (-1);
+
+       if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, 
+                   &nevents, &ts)) == -1) {
+               if (errno == EINTR) {
+                       evsignal_process();
+                       return (0);
+               } else if (errno == ETIME) {
+                       if (nevents == 0)
+                               return (0);
+               } else {
+                       perror("port_getn");
+                       return (-1);
+               }
+       } else if (evsignal_caught) {
+               evsignal_process();
+       }
+       
+       event_debug(("%s: port_getn reports %d events", __func__, nevents));
+
+       for (i = 0; i < nevents; ++i) {
+               struct event *ev;
+               struct fd_info *fdi;
+               port_event_t *pevt = &pevtlist[i];
+               int fd = (int) pevt->portev_object;
+
+               check_evportop(epdp);
+               check_event(pevt);
+
+               /*
+                * Figure out what kind of event it was 
+                * (because we have to pass this to the callback)
+                */
+               res = 0;
+               if (pevt->portev_events & POLLIN)
+                       res |= EV_READ;
+               if (pevt->portev_events & POLLOUT)
+                       res |= EV_WRITE;
+
+               assert(epdp->ed_nevents > fd);
+               fdi = &(epdp->ed_fds[fd]);
+
+               /*
+                * We now check for each of the possible events (READ or WRITE).
+                * If the event is not persistent, then we delete it. Then, we
+                * activate the event (which will cause its callback to be
+                * executed).
+                */
+
+               if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) {
+                       if (!(ev->ev_events & EV_PERSIST))
+                               event_del(ev);
+                       event_active(ev, res, 1);
+               }
+
+               if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) {
+                       if (!(ev->ev_events & EV_PERSIST))
+                               event_del(ev);
+                       event_active(ev, res, 1);
+               }
+
+               /*
+                * If there are still events (they haven't been deleted), then
+                * we must reassociate the port, since the event port interface
+                * dissociates them automatically. 
+                *
+                * But we can't do it right away, because the event hasn't
+                * handled this event yet, so of course there's still data
+                * waiting!
+                */
+               if(FDI_HAS_EVENTS(fdi)) {
+                       epdp->ed_pending[i] = fdi;
+               }
+       } /* end of all events gotten */
+
+       check_evportop(epdp);
+
+       if (evsignal_recalc(&epdp->ed_sigmask) == -1)
+               return (-1);
+
+       return (0);
+}
+
+
+/*
+ * Copied from the version in select.c
+ */
+
+static int
+evport_recalc(struct event_base *base, void *arg, int max)
+{
+       struct evport_data *evpd = arg;
+       check_evportop(evpd);
+       return (evsignal_recalc(&evpd->ed_sigmask));
+}
+
+
+/*
+ * Adds the given event (so that you will be notified when it happens via
+ * the callback function).
+ */
+
+static int
+evport_add(void *arg, struct event *ev)
+{
+       struct evport_data *evpd = arg;
+       struct fd_info *fdi;
+       int factor;
+
+       check_evportop(evpd);
+
+       /*
+        * Delegate, if it's not ours to handle.
+        */
+       if (ev->ev_events & EV_SIGNAL)
+               return (evsignal_add(&evpd->ed_sigmask, ev));
+
+       /*
+        * If necessary, grow the file descriptor info table
+        */
+
+       factor = 1;
+       while (ev->ev_fd >= factor * evpd->ed_nevents)
+               factor *= 2;
+
+       if (factor > 1) {
+               if (-1 == grow(evpd, factor)) {
+                       return (-1);
+               }
+       }
+
+       fdi = &evpd->ed_fds[ev->ev_fd];
+       if (ev->ev_events & EV_READ)
+               fdi->fdi_revt = ev;
+       if (ev->ev_events & EV_WRITE)
+               fdi->fdi_wevt = ev;
+
+       return reassociate(evpd, fdi, ev->ev_fd);
+}
+
+/*
+ * Removes the given event from the list of events to wait for.
+ */
+
+static int
+evport_del(void *arg, struct event *ev)
+{
+       struct evport_data *evpd = arg;
+       struct fd_info *fdi;
+
+       check_evportop(evpd);
+
+       /*
+        * Delegate, if it's not ours to handle
+        */
+       if (ev->ev_events & EV_SIGNAL) {
+               return (evsignal_del(&evpd->ed_sigmask, ev));
+       }
+
+       if (evpd->ed_nevents < ev->ev_fd) {
+               return (-1);
+       }
+
+
+       fdi = &evpd->ed_fds[ev->ev_fd];
+       if (ev->ev_events & EV_READ)
+               fdi->fdi_revt = NULL;
+       if (ev->ev_events & EV_WRITE)
+               fdi->fdi_wevt = NULL;
+
+       return reassociate(evpd, fdi, ev->ev_fd);
+}
+
+