Edit

IABSD.fr/xenocara/xserver/os/ospoll.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2026-05-08 11:12:08
    Hash : f0e0a48c
    Message : Update to xserver 21.1.22. The security fixes were already committed.

  • xserver/os/ospoll.c
  • /*
     * 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 <assert.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include "misc.h"               /* for typedef of pointer */
    #include "ospoll.h"
    #include "list.h"
    
    #if !HAVE_OSPOLL && defined(HAVE_POLLSET_CREATE)
    #include <sys/pollset.h>
    #define POLLSET         1
    #define HAVE_OSPOLL     1
    #endif
    
    #if !HAVE_OSPOLL && defined(HAVE_PORT_CREATE)
    #include <port.h>
    #include <poll.h>
    #define PORT            1
    #define HAVE_OSPOLL     1
    #endif
    
    #if !HAVE_OSPOLL && defined(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 POLLSET
    
    // pollset-based implementation (as seen on AIX)
    struct ospollfd {
        int                 fd;
        int                 xevents;
        short               revents;
        enum ospoll_trigger trigger;
        void                (*callback)(int fd, int xevents, void *data);
        void                *data;
    };
    
    struct ospoll {
        pollset_t           ps;
        struct ospollfd     *fds;
        int                 num;
        int                 size;
    };
    
    #endif
    
    #if EPOLL || PORT
    
    /* epoll-based implementation */
    struct ospollfd {
        int                 fd;
        int                 xevents;
        enum ospoll_trigger trigger;
        void                (*callback)(int fd, int xevents, void *data);
        void                *data;
        struct xorg_list    deleted;
    };
    
    struct ospoll {
        int                 epoll_fd;
        struct ospollfd     **fds;
        int                 num;
        int                 size;
        struct xorg_list    deleted;
    };
    
    #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;
        Bool                changed;
    };
    
    #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 || PORT
            int t = ospoll->fds[m]->fd;
    #endif
    #if POLL || POLLSET
            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);
    }
    
    #if EPOLL || PORT
    static void
    ospoll_clean_deleted(struct ospoll *ospoll)
    {
        struct ospollfd     *osfd, *tmp;
    
        xorg_list_for_each_entry_safe(osfd, tmp, &ospoll->deleted, deleted) {
            xorg_list_del(&osfd->deleted);
            free(osfd);
        }
    }
    #endif
    
    /* 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 POLLSET
        struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
    
        ospoll->ps = pollset_create(-1);
        if (ospoll->ps < 0) {
            free (ospoll);
            return NULL;
        }
        return ospoll;
    #endif
    #if PORT
        struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
    
        ospoll->epoll_fd = port_create();
        if (ospoll->epoll_fd < 0) {
            free (ospoll);
            return NULL;
        }
        xorg_list_init(&ospoll->deleted);
        return ospoll;
    #endif
    #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;
        }
        xorg_list_init(&ospoll->deleted);
        return ospoll;
    #endif
    #if POLL
        return calloc(1, sizeof (struct ospoll));
    #endif
    }
    
    void
    ospoll_destroy(struct ospoll *ospoll)
    {
    #if POLLSET
        if (ospoll) {
            assert (ospoll->num == 0);
            pollset_destroy(ospoll->ps);
            free(ospoll->fds);
            free(ospoll);
        }
    #endif
    #if EPOLL || PORT
        if (ospoll) {
            assert (ospoll->num == 0);
            close(ospoll->epoll_fd);
            ospoll_clean_deleted(ospoll);
            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 POLLSET
        if (pos < 0) {
            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)
                    return FALSE;
                ospoll->fds = new_fds;
                ospoll->size = new_size;
            }
            pos = -pos - 1;
            array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
            ospoll->num++;
    
            ospoll->fds[pos].fd = fd;
            ospoll->fds[pos].xevents = 0;
            ospoll->fds[pos].revents = 0;
        }
        ospoll->fds[pos].trigger = trigger;
        ospoll->fds[pos].callback = callback;
        ospoll->fds[pos].data = data;
    #endif
    #if PORT
        struct ospollfd *osfd;
    
        if (pos < 0) {
            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;
            }
    
            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 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->changed = TRUE;
    
            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 POLLSET
            struct ospollfd *osfd = &ospoll->fds[pos];
            struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
            pollset_ctl(ospoll->ps, &ctl, 1);
    
            array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
            ospoll->num--;
    #endif
    #if PORT
            struct ospollfd *osfd = ospoll->fds[pos];
            port_dissociate(ospoll->epoll_fd, PORT_SOURCE_FD, fd);
    
            array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
            ospoll->num--;
            osfd->callback = NULL;
            osfd->data = NULL;
            xorg_list_add(&osfd->deleted, &ospoll->deleted);
    #endif
    #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--;
            osfd->callback = NULL;
            osfd->data = NULL;
            xorg_list_add(&osfd->deleted, &ospoll->deleted);
    #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--;
            ospoll->changed = TRUE;
    #endif
        }
    }
    
    #if PORT
    static void
    epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd)
    {
        int events = 0;
        if (osfd->xevents & X_NOTIFY_READ)
            events |= POLLIN;
        if (osfd->xevents & X_NOTIFY_WRITE)
            events |= POLLOUT;
        port_associate(ospoll->epoll_fd, PORT_SOURCE_FD, osfd->fd, events, osfd);
    }
    #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 POLLSET
            struct poll_ctl ctl = { .cmd = PS_MOD, .fd = fd };
            if (xevents & X_NOTIFY_READ) {
                ctl.events |= POLLIN;
                ospoll->fds[pos].revents &= ~POLLIN;
            }
            if (xevents & X_NOTIFY_WRITE) {
                ctl.events |= POLLOUT;
                ospoll->fds[pos].revents &= ~POLLOUT;
            }
            pollset_ctl(ospoll->ps, &ctl, 1);
            ospoll->fds[pos].xevents |= xevents;
    #endif
    #if EPOLL || PORT
            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;
                ospoll->osfds[pos].revents &= ~POLLIN;
            }
            if (xevents & X_NOTIFY_WRITE) {
                ospoll->fds[pos].events |= POLLOUT;
                ospoll->osfds[pos].revents &= ~POLLOUT;
            }
    #endif
        }
    }
    
    void
    ospoll_mute(struct ospoll *ospoll, int fd, int xevents)
    {
        int pos = ospoll_find(ospoll, fd);
    
        if (pos >= 0) {
    #if POLLSET
            struct ospollfd *osfd = &ospoll->fds[pos];
            osfd->xevents &= ~xevents;
            struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
            pollset_ctl(ospoll->ps, &ctl, 1);
            if (osfd->xevents) {
                ctl.cmd = PS_ADD;
                if (osfd->xevents & X_NOTIFY_READ) {
                    ctl.events |= POLLIN;
                }
                if (osfd->xevents & X_NOTIFY_WRITE) {
                    ctl.events |= POLLOUT;
                }
                pollset_ctl(ospoll->ps, &ctl, 1);
            }
    #endif
    #if EPOLL || PORT
            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 POLLSET
    #define MAX_EVENTS      256
        struct pollfd events[MAX_EVENTS];
    
        nready = pollset_poll(ospoll->ps, events, MAX_EVENTS, timeout);
        for (int i = 0; i < nready; i++) {
            struct pollfd *ev = &events[i];
            int pos = ospoll_find(ospoll, ev->fd);
            struct ospollfd *osfd = &ospoll->fds[pos];
            short revents = ev->revents;
            short oldevents = osfd->revents;
    
            osfd->revents = (revents & (POLLIN|POLLOUT));
            if (osfd->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;
                osfd->callback(osfd->fd, xevents, osfd->data);
            }
        }
    #endif
    #if PORT
    #define MAX_EVENTS      256
        port_event_t events[MAX_EVENTS];
        uint_t nget = 1;
        timespec_t port_timeout = {
            .tv_sec = timeout / 1000,
            .tv_nsec = (timeout % 1000) * 1000000
        };
    
        nready = 0;
        if (port_getn(ospoll->epoll_fd, events, MAX_EVENTS, &nget, &port_timeout)
            == 0) {
            nready = nget;
        }
        for (int i = 0; i < nready; i++) {
            port_event_t *ev = &events[i];
            struct ospollfd *osfd = ev->portev_user;
            uint32_t revents = ev->portev_events;
            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;
    
            if (osfd->callback)
                osfd->callback(osfd->fd, xevents, osfd->data);
    
            if (osfd->trigger == ospoll_trigger_level &&
                !xorg_list_is_empty(&osfd->deleted)) {
                epoll_mod(ospoll, osfd);
            }
        }
        ospoll_clean_deleted(ospoll);
    #endif
    #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;
    
            if (osfd->callback)
                osfd->callback(osfd->fd, xevents, osfd->data);
        }
        ospoll_clean_deleted(ospoll);
    #endif
    #if POLL
        nready = xserver_poll(ospoll->fds, ospoll->num, timeout);
        ospoll->changed = FALSE;
        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);
    
                    /* Check to see if the arrays have changed, and just go back
                     * around again
                     */
                    if (ospoll->changed)
                        break;
                }
            }
        }
    #endif
        return nready;
    }
    
    void
    ospoll_reset_events(struct ospoll *ospoll, int fd)
    {
    #if POLLSET
        int pos = ospoll_find(ospoll, fd);
    
        if (pos < 0)
            return;
    
        ospoll->fds[pos].revents = 0;
    #endif
    #if PORT
        int pos = ospoll_find(ospoll, fd);
    
        if (pos < 0)
            return;
    
        epoll_mod(ospoll, ospoll->fds[pos]);
    #endif
    #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 POLLSET
        return ospoll->fds[pos].data;
    #endif
    #if EPOLL || PORT
        return ospoll->fds[pos]->data;
    #endif
    #if POLL
        return ospoll->osfds[pos].data;
    #endif
    }