[PATCH xserver 17/23] os: Add ospoll interface

Keith Packard keithp at keithp.com
Thu May 26 23:59:52 UTC 2016


This provides a wrapper around poll, epoll or WSAPoll providing a
callback-based interface for monitoring activity on a large set of
file descriptors.

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 configure.ac            |   2 +-
 include/dix-config.h.in |   9 +
 os/Makefile.am          |   1 +
 os/ospoll.c             | 439 ++++++++++++++++++++++++++++++++++++++++++++++++
 os/ospoll.h             | 144 ++++++++++++++++
 5 files changed, 594 insertions(+), 1 deletion(-)
 create mode 100644 os/ospoll.c
 create mode 100644 os/ospoll.h

diff --git a/configure.ac b/configure.ac
index 1a9aa81..d078190 100644
--- a/configure.ac
+++ b/configure.ac
@@ -219,7 +219,7 @@ dnl Checks for library functions.
 AC_CHECK_FUNCS([backtrace ffs geteuid getuid issetugid getresuid \
 	getdtablesize getifaddrs getpeereid getpeerucred getprogname getzoneid \
 	mmap posix_fallocate seteuid shmctl64 strncasecmp vasprintf vsnprintf \
-	walkcontext setitimer])
+	walkcontext setitimer poll epoll_create1 WSAPoll])
 AC_REPLACE_FUNCS([reallocarray strcasecmp strcasestr strlcat strlcpy strndup])
 
 AC_CHECK_DECLS([program_invocation_short_name], [], [], [[#include <errno.h>]])
diff --git a/include/dix-config.h.in b/include/dix-config.h.in
index fc7d1a1..11025e0 100644
--- a/include/dix-config.h.in
+++ b/include/dix-config.h.in
@@ -527,4 +527,13 @@
 /* Use input thread */
 #undef INPUTTHREAD
 
+/* Have poll() */
+#undef HAVE_POLL
+
+/* Have epoll_create1() */
+#undef HAVE_EPOLL_CREATE1
+
+/* Have WSAPoll() */
+#undef HAVE_WSAPOLL
+
 #endif /* _DIX_CONFIG_H_ */
diff --git a/os/Makefile.am b/os/Makefile.am
index fc49a73..6835142 100644
--- a/os/Makefile.am
+++ b/os/Makefile.am
@@ -20,6 +20,7 @@ libos_la_SOURCES = 	\
 	oscolor.c	\
 	osdep.h		\
 	osinit.c	\
+	ospoll.c	\
 	utils.c		\
 	xdmauth.c	\
 	xsha1.c		\
diff --git a/os/ospoll.c b/os/ospoll.c
new file mode 100644
index 0000000..c3e8d5b
--- /dev/null
+++ b/os/ospoll.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright © 2016 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <X11/X.h>
+#include <X11/Xproto.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "misc.h"               /* for typedef of pointer */
+#include "ospoll.h"
+#include "list.h"
+
+#if !HAVE_OSPOLL && HAVE_EPOLL_CREATE1
+#define EPOLL           1
+#define HAVE_OSPOLL     1
+#endif
+
+#if !HAVE_OSPOLL && HAVE_POLL
+#define POLL            1
+#define HAVE_OSPOLL     1
+#endif
+
+#if !HAVE_OSPOLL && HAVE_WSAPOLL
+#define WSAPOLL         1
+#define HAVE_OSPOLL     1
+#endif
+
+#if EPOLL
+#include <sys/epoll.h>
+
+/* epoll-based implementation */
+struct ospollfd {
+    int                 fd;
+    short               events;
+    enum ospoll_trigger trigger;
+    void                (*callback)(int fd, short revents, void *data);
+    void                *data;
+};
+
+struct ospoll {
+    int                 epoll_fd;
+    struct ospollfd     **fds;
+    int                 num;
+    int                 size;
+};
+
+#endif
+
+#if WSAPOLL
+/* WSAPoll is just like poll, but with a different name (for reasons) */
+#include <X11/Xwinsock.h>
+#define POLL            1
+#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
+#endif
+
+#if POLL
+
+/* poll-based implementation */
+struct ospollfd {
+    short               revents;
+    enum ospoll_trigger trigger;
+    void                (*callback)(int fd, short revents, void *data);
+    void                *data;
+};
+
+struct ospoll {
+    struct pollfd       *fds;
+    struct ospollfd     *osfds;
+    int                 num;
+    int                 size;
+};
+
+#endif
+
+/* Binary search for the specified file descriptor
+ *
+ * Returns position if found
+ * Returns -position - 1 if not found
+ */
+
+static int
+ospoll_find(struct ospoll *ospoll, int fd)
+{
+    int lo = 0;
+    int hi = ospoll->num - 1;
+
+    while (lo <= hi) {
+        int m = (lo + hi) >> 1;
+#if EPOLL
+        int t = ospoll->fds[m]->fd;
+#endif
+#if POLL
+        int t = ospoll->fds[m].fd;
+#endif
+
+        if (t < fd)
+            lo = m + 1;
+        else if (t > fd)
+            hi = m - 1;
+        else
+            return m;
+    }
+    return -(lo + 1);
+}
+
+/* Insert an element into an array
+ *
+ * base: base address of array
+ * num:  number of elements in the array before the insert
+ * size: size of each element
+ * pos:  position to insert at
+ */
+static inline void
+array_insert(void *base, size_t num, size_t size, size_t pos)
+{
+    char *b = base;
+
+    memmove(b + (pos+1) * size,
+            b + pos * size,
+            (num - pos) * size);
+}
+
+/* Delete an element from an array
+ *
+ * base: base address of array
+ * num:  number of elements in the array before the delete
+ * size: size of each element
+ * pos:  position to delete from
+ */
+static inline void
+array_delete(void *base, size_t num, size_t size, size_t pos)
+{
+    char *b = base;
+
+    memmove(b + pos * size, b + (pos + 1) * size,
+            (num - pos - 1) * size);
+}
+
+
+struct ospoll *
+ospoll_create(void)
+{
+#if EPOLL
+    struct ospoll       *ospoll = calloc(1, sizeof (struct ospoll));
+
+    ospoll->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+    if (ospoll->epoll_fd < 0) {
+        free (ospoll);
+        return NULL;
+    }
+    return ospoll;
+#endif
+#if POLL
+    return calloc(1, sizeof (struct ospoll));
+#endif
+}
+
+void
+ospoll_destroy(struct ospoll *ospoll)
+{
+#if EPOLL
+    if (ospoll) {
+        assert (ospoll->num == 0);
+        close(ospoll->epoll_fd);
+        free(ospoll->fds);
+        free(ospoll);
+    }
+#endif
+#if POLL
+    if (ospoll) {
+        assert (ospoll->num == 0);
+        free (ospoll->fds);
+        free (ospoll->osfds);
+        free (ospoll);
+    }
+#endif
+}
+
+Bool
+ospoll_add(struct ospoll *ospoll, int fd,
+           enum ospoll_trigger trigger,
+           void (*callback)(int fd, short revents, void *data),
+           void *data)
+{
+    int pos = ospoll_find(ospoll, fd);
+#if EPOLL
+    struct ospollfd *osfd;
+
+    if (pos < 0) {
+
+        struct epoll_event ev;
+
+        osfd = calloc(1, sizeof (struct ospollfd));
+        if (!osfd)
+            return FALSE;
+
+        if (ospoll->num >= ospoll->size) {
+            struct ospollfd **new_fds;
+            int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
+
+            new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
+            if (!new_fds) {
+                free (osfd);
+                return FALSE;
+            }
+            ospoll->fds = new_fds;
+            ospoll->size = new_size;
+        }
+
+        ev.events = 0;
+        ev.data.ptr = osfd;
+        if (trigger == ospoll_trigger_edge)
+            ev.events |= EPOLLET;
+        if (epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+            free(osfd);
+            return FALSE;
+        }
+        osfd->fd = fd;
+        osfd->events = 0;
+
+        pos = -pos - 1;
+        array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
+        ospoll->fds[pos] = osfd;
+        ospoll->num++;
+    } else {
+        osfd = ospoll->fds[pos];
+    }
+    osfd->data = data;
+    osfd->callback = callback;
+    osfd->trigger = trigger;
+#endif
+#if POLL
+    if (pos < 0) {
+        if (ospoll->num == ospoll->size) {
+            struct pollfd   *new_fds;
+            struct ospollfd *new_osfds;
+            int             new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
+
+            new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
+            if (!new_fds)
+                return FALSE;
+            ospoll->fds = new_fds;
+            new_osfds = reallocarray(ospoll->osfds, new_size, sizeof (ospoll->osfds[0]));
+            if (!new_osfds)
+                return FALSE;
+            ospoll->osfds = new_osfds;
+            ospoll->size = new_size;
+        }
+        pos = -pos - 1;
+        array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
+        array_insert(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos);
+        ospoll->num++;
+
+        ospoll->fds[pos].fd = fd;
+        ospoll->fds[pos].events = 0;
+        ospoll->fds[pos].revents = 0;
+        ospoll->osfds[pos].revents = 0;
+    }
+    ospoll->osfds[pos].trigger = trigger;
+    ospoll->osfds[pos].callback = callback;
+    ospoll->osfds[pos].data = data;
+#endif
+    return TRUE;
+}
+
+void
+ospoll_remove(struct ospoll *ospoll, int fd)
+{
+    int pos = ospoll_find(ospoll, fd);
+
+    pos = ospoll_find(ospoll, fd);
+    if (pos >= 0) {
+#if EPOLL
+        struct ospollfd *osfd = ospoll->fds[pos];
+        struct epoll_event ev;
+        ev.events = 0;
+        ev.data.ptr = osfd;
+        (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_DEL, fd, &ev);
+
+        array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
+        ospoll->num--;
+        free (osfd);
+#endif
+#if POLL
+        array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
+        array_delete(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos);
+        ospoll->num--;
+#endif
+    }
+}
+
+#if EPOLL
+static void
+epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd)
+{
+    struct epoll_event ev;
+    ev.events = 0;
+    if (osfd->events & POLLIN)
+        ev.events |= EPOLLIN;
+    if (osfd->events & POLLOUT)
+        ev.events |= EPOLLOUT;
+    if (osfd->trigger == ospoll_trigger_edge)
+        ev.events |= EPOLLET;
+    ev.data.ptr = osfd;
+    (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_MOD, osfd->fd, &ev);
+}
+#endif
+
+void
+ospoll_listen(struct ospoll *ospoll, int fd, short events)
+{
+    int pos = ospoll_find(ospoll, fd);
+
+    if (pos >= 0) {
+#if EPOLL
+        struct ospollfd *osfd = ospoll->fds[pos];
+        osfd->events |= events;
+        epoll_mod(ospoll, osfd);
+#endif
+#if POLL
+        ospoll->fds[pos].events |= events;
+#endif
+    }
+}
+
+void
+ospoll_mute(struct ospoll *ospoll, int fd, short events)
+{
+    int pos = ospoll_find(ospoll, fd);
+
+    if (pos >= 0) {
+#if EPOLL
+        struct ospollfd *osfd = ospoll->fds[pos];
+        osfd->events &= ~events;
+        epoll_mod(ospoll, osfd);
+#endif
+#if POLL
+        ospoll->fds[pos].events &= ~events;
+#endif
+    }
+}
+
+
+int
+ospoll_wait(struct ospoll *ospoll, int timeout)
+{
+    int nready;
+#if EPOLL
+#define MAX_EVENTS      256
+    struct epoll_event events[MAX_EVENTS];
+    int i;
+
+    nready = epoll_wait(ospoll->epoll_fd, events, MAX_EVENTS, timeout);
+    for (i = 0; i < nready; i++) {
+        struct epoll_event *ev = &events[i];
+        struct ospollfd *osfd = ev->data.ptr;
+        short revents = 0;
+
+        if (ev->events & EPOLLIN)
+            revents |= POLLIN;
+        if (ev->events & EPOLLOUT)
+            revents |= POLLOUT;
+        if (ev->events & EPOLLHUP)
+            revents |= POLLHUP;
+        if (ev->events & ~(EPOLLIN|EPOLLOUT|EPOLLHUP))
+            revents |= POLLERR;
+        if (revents)
+            osfd->callback(osfd->fd, revents, osfd->data);
+    }
+#endif
+#if POLL
+    nready = poll (ospoll->fds, ospoll->num, timeout);
+    if (nready > 0) {
+        int f;
+        for (f = 0; f < ospoll->num; f++) {
+            short revents = ospoll->fds[f].revents;
+            short oldevents = ospoll->osfds[f].revents;
+
+            ospoll->osfds[f].revents = revents;
+            if (ospoll->osfds[f].trigger == ospoll_trigger_edge)
+                revents &= ~oldevents;
+            if (revents)
+                ospoll->osfds[f].callback(ospoll->fds[f].fd, revents,
+                                          ospoll->osfds[f].data);
+        }
+    }
+#endif
+    return nready;
+}
+
+void
+ospoll_reset_events(struct ospoll *ospoll, int fd)
+{
+#if POLL
+    int pos = ospoll_find(ospoll, fd);
+
+    if (pos < 0)
+        return;
+
+    ospoll->osfds[pos].revents = 0;
+#endif
+}
+
+void *
+ospoll_data(struct ospoll *ospoll, int fd)
+{
+    int pos = ospoll_find(ospoll, fd);
+
+    if (pos < 0)
+        return NULL;
+#if EPOLL
+    return ospoll->fds[pos]->data;
+#endif
+#if POLL
+    return ospoll->osfds[pos].data;
+#endif
+}
diff --git a/os/ospoll.h b/os/ospoll.h
new file mode 100644
index 0000000..026e214
--- /dev/null
+++ b/os/ospoll.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2016 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _OSPOLL_H_
+#define _OSPOLL_H_
+
+#include <poll.h>
+
+/* Forward declaration */
+struct ospoll;
+
+/**
+ * ospoll_wait trigger mode
+ *
+ * @ospoll_trigger_edge
+ *      Trigger only when going from no data available
+ *      to data available.
+ *
+ * @ospoll_trigger_level
+ *      Trigger whenever there is data available
+ */
+enum ospoll_trigger {
+    ospoll_trigger_edge,
+    ospoll_trigger_level
+};
+
+/**
+ * Create a new ospoll structure
+ */
+struct ospoll *
+ospoll_create(void);
+
+/**
+ * Destroy an ospoll structure
+ *
+ * @param       ospoll          ospoll to destroy
+ */
+void
+ospoll_destroy(struct ospoll *ospoll);
+
+/**
+ * Add a file descriptor to monitor
+ *
+ * @param       ospoll          ospoll to add to
+ * @param       fd              File descriptor to monitor
+ * @param       trigger         Trigger mode for ospoll_wait
+ * @param       callback        Function to call when triggered
+ * @param       data            Extra data to pass callback
+ */
+Bool
+ospoll_add(struct ospoll *ospoll, int fd,
+           enum ospoll_trigger trigger,
+           void (*callback)(int fd, short revents, void *data),
+           void *data);
+
+/**
+ * Remove a monitored file descriptor
+ *
+ * @param       ospoll          ospoll to remove from
+ * @param       fd              File descriptor to stop monitoring
+ */
+void
+ospoll_remove(struct ospoll *ospoll, int fd);
+
+/**
+ * Listen on additional events
+ *
+ * @param       ospoll          ospoll monitoring fd
+ * @param       fd              File descriptor to change
+ * @param       events          Additional events to trigger on
+ */
+void
+ospoll_listen(struct ospoll *ospoll, int fd, short events);
+
+/**
+ * Stop listening on events
+ *
+ * @param       ospoll          ospoll monitoring fd
+ * @param       fd              File descriptor to change
+ * @param       events          events to stop triggering on
+ */
+void
+ospoll_mute(struct ospoll *ospoll, int fd, short events);
+
+/**
+ * Wait for events
+ *
+ * @param       ospoll          ospoll to wait on
+ * @param       timeout         < 0 wait forever
+ *                              = 0 check and return
+ *                              > 0 timeout in milliseconds
+ * @return      < 0 error
+ *              = 0 timeout
+ *              > 0 number of events delivered
+ */
+int
+ospoll_wait(struct ospoll *ospoll, int timeout);
+
+/**
+ * Reset edge trigger status
+ *
+ * @param       ospoll          ospoll monitoring fd
+ * @param       fd              file descriptor
+ *
+ * ospoll_reset_events resets the state of an edge-triggered
+ * fd so that ospoll_wait calls will report events again.
+ *
+ * Call this after a read/recv operation reports no more data available.
+ */
+void
+ospoll_reset_events(struct ospoll *ospoll, int fd);
+
+/**
+ * Fetch the data associated with an fd
+ *
+ * @param       ospoll          ospoll monitoring fd
+ * @param       fd              file descriptor
+ *
+ * @return      data parameter passed to ospoll_add call on
+ *              this file descriptor
+ */
+void *
+ospoll_data(struct ospoll *ospoll, int fd);
+
+#endif /* _OSPOLL_H_ */
-- 
2.8.0.rc3



More information about the xorg-devel mailing list