diff options
Diffstat (limited to 'os/ospoll.c')
-rw-r--r-- | os/ospoll.c | 442 |
1 files changed, 442 insertions, 0 deletions
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 +} |