summaryrefslogtreecommitdiff
path: root/os
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2016-05-26 10:20:45 -0700
committerAdam Jackson <ajax@redhat.com>2016-07-21 15:04:47 -0400
commitd6eff3c31e8289881a3aa9b858e5710d0f741db0 (patch)
tree4770bb14529c4b7dd1764b58cd2f2deb59efe7c9 /os
parentd403aca70a07e1401cb93738f1af5961582a2e47 (diff)
os: Add ospoll interface [v2]
This provides a wrapper around poll or epoll providing a callback-based interface for monitoring activity on a large set of file descriptors. v2: use xserver_poll API instead of poll. Don't use WSAPoll as that is broken. Signed-off-by: Keith Packard <keithp@keithp.com> Reviewed-by: Adam Jackson <ajax@redhat.com>
Diffstat (limited to 'os')
-rw-r--r--os/Makefile.am1
-rw-r--r--os/ospoll.c442
-rw-r--r--os/ospoll.h142
3 files changed, 585 insertions, 0 deletions
diff --git a/os/Makefile.am b/os/Makefile.am
index c449076f8..d97ba30fb 100644
--- a/os/Makefile.am
+++ b/os/Makefile.am
@@ -22,6 +22,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 000000000..3c2b80b39
--- /dev/null
+++ b/os/ospoll.c
@@ -0,0 +1,442 @@
+/*
+ * 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
+#include <sys/epoll.h>
+#define EPOLL 1
+#define HAVE_OSPOLL 1
+#endif
+
+#if !HAVE_OSPOLL
+#include "xserver_poll.h"
+#define POLL 1
+#define HAVE_OSPOLL 1
+#endif
+
+#if EPOLL
+#include <sys/epoll.h>
+
+/* epoll-based implementation */
+struct ospollfd {
+ int fd;
+ int xevents;
+ enum ospoll_trigger trigger;
+ void (*callback)(int fd, int xevents, void *data);
+ void *data;
+};
+
+struct ospoll {
+ int epoll_fd;
+ struct ospollfd **fds;
+ int num;
+ int size;
+};
+
+#endif
+
+#if POLL
+
+/* poll-based implementation */
+struct ospollfd {
+ short revents;
+ enum ospoll_trigger trigger;
+ void (*callback)(int fd, int 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, int xevents, 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->xevents = 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->xevents & X_NOTIFY_READ)
+ ev.events |= EPOLLIN;
+ if (osfd->xevents & X_NOTIFY_WRITE)
+ 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, int xevents)
+{
+ int pos = ospoll_find(ospoll, fd);
+
+ if (pos >= 0) {
+#if EPOLL
+ struct ospollfd *osfd = ospoll->fds[pos];
+ osfd->xevents |= xevents;
+ epoll_mod(ospoll, osfd);
+#endif
+#if POLL
+ if (xevents & X_NOTIFY_READ)
+ ospoll->fds[pos].events |= POLLIN;
+ if (xevents & X_NOTIFY_WRITE)
+ ospoll->fds[pos].events |= POLLOUT;
+#endif
+ }
+}
+
+void
+ospoll_mute(struct ospoll *ospoll, int fd, int xevents)
+{
+ int pos = ospoll_find(ospoll, fd);
+
+ if (pos >= 0) {
+#if EPOLL
+ struct ospollfd *osfd = ospoll->fds[pos];
+ osfd->xevents &= ~xevents;
+ epoll_mod(ospoll, osfd);
+#endif
+#if POLL
+ if (xevents & X_NOTIFY_READ)
+ ospoll->fds[pos].events &= ~POLLIN;
+ if (xevents & X_NOTIFY_WRITE)
+ ospoll->fds[pos].events &= ~POLLOUT;
+#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;
+ uint32_t revents = ev->events;
+ int xevents = 0;
+
+ if (revents & EPOLLIN)
+ xevents |= X_NOTIFY_READ;
+ if (revents & EPOLLOUT)
+ xevents |= X_NOTIFY_WRITE;
+ if (revents & (~(EPOLLIN|EPOLLOUT)))
+ xevents |= X_NOTIFY_ERROR;
+
+ osfd->callback(osfd->fd, xevents, osfd->data);
+ }
+#endif
+#if POLL
+ nready = xserver_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 & (POLLIN|POLLOUT));
+ if (ospoll->osfds[f].trigger == ospoll_trigger_edge)
+ revents &= ~oldevents;
+ if (revents) {
+ int xevents = 0;
+ if (revents & POLLIN)
+ xevents |= X_NOTIFY_READ;
+ if (revents & POLLOUT)
+ xevents |= X_NOTIFY_WRITE;
+ if (revents & (~(POLLIN|POLLOUT)))
+ xevents |= X_NOTIFY_ERROR;
+ ospoll->osfds[f].callback(ospoll->fds[f].fd, xevents,
+ 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 000000000..cdc45f4d9
--- /dev/null
+++ b/os/ospoll.h
@@ -0,0 +1,142 @@
+/*
+ * 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_
+
+/* 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, int xevents, 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, int xevents);
+
+/**
+ * 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, int xevents);
+
+/**
+ * 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_ */