[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