diff options
Diffstat (limited to 'unix/utils/pollwrap.c')
-rw-r--r-- | unix/utils/pollwrap.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/unix/utils/pollwrap.c b/unix/utils/pollwrap.c new file mode 100644 index 00000000..51121e36 --- /dev/null +++ b/unix/utils/pollwrap.c @@ -0,0 +1,190 @@ +/* + * Wrapper system around poll() that lets me treat it more or less + * like select(), but avoiding the inherent limitation of select() + * that it can't handle the full range of fds that are capable of + * existing. + * + * The pollwrapper structure contains the 'struct pollfd' array passed + * to poll() itself, and also a tree234 that maps each fd to its + * location in the list, which makes it convenient to add or remove + * individual fds from the system or change what events you're + * watching for on them. So the API is _shaped_ basically like select, + * even if none of the details are identical: from outside this + * module, a pollwrapper can be used wherever you'd otherwise have had + * an fd_set. + * + * Also, this module translate between the simple select r/w/x + * classification and the richer poll flags. We have to stick to r/w/x + * in this code base, because it ports to other systems where that's + * all you get. + */ + +/* On some systems this is needed to get poll.h to define eg.. POLLRDNORM */ +#define _XOPEN_SOURCE + +#include <poll.h> + +#include "putty.h" +#include "tree234.h" + +struct pollwrapper { + struct pollfd *fds; + size_t nfd, fdsize; + tree234 *fdtopos; +}; + +typedef struct pollwrap_fdtopos pollwrap_fdtopos; +struct pollwrap_fdtopos { + int fd; + size_t pos; +}; + +static int pollwrap_fd_cmp(void *av, void *bv) +{ + pollwrap_fdtopos *a = (pollwrap_fdtopos *)av; + pollwrap_fdtopos *b = (pollwrap_fdtopos *)bv; + return a->fd < b->fd ? -1 : a->fd > b->fd ? +1 : 0; +} + +pollwrapper *pollwrap_new(void) +{ + pollwrapper *pw = snew(pollwrapper); + pw->fdsize = 16; + pw->nfd = 0; + pw->fds = snewn(pw->fdsize, struct pollfd); + pw->fdtopos = newtree234(pollwrap_fd_cmp); + return pw; +} + +void pollwrap_free(pollwrapper *pw) +{ + pollwrap_clear(pw); + freetree234(pw->fdtopos); + sfree(pw->fds); + sfree(pw); +} + +void pollwrap_clear(pollwrapper *pw) +{ + pw->nfd = 0; + for (pollwrap_fdtopos *f2p; + (f2p = delpos234(pw->fdtopos, 0)) != NULL ;) + sfree(f2p); +} + +void pollwrap_add_fd_events(pollwrapper *pw, int fd, int events) +{ + pollwrap_fdtopos *f2p, f2p_find; + + assert(fd >= 0); + + f2p_find.fd = fd; + f2p = find234(pw->fdtopos, &f2p_find, NULL); + if (!f2p) { + sgrowarray(pw->fds, pw->fdsize, pw->nfd); + size_t index = pw->nfd++; + pw->fds[index].fd = fd; + pw->fds[index].events = pw->fds[index].revents = 0; + + f2p = snew(pollwrap_fdtopos); + f2p->fd = fd; + f2p->pos = index; + pollwrap_fdtopos *added = add234(pw->fdtopos, f2p); + assert(added == f2p); + } + + pw->fds[f2p->pos].events |= events; +} + +/* Omit any of the POLL{RD,WR}{NORM,BAND} flag values that are still + * not defined by poll.h, just in case */ +#ifndef POLLRDNORM +#define POLLRDNORM 0 +#endif +#ifndef POLLRDBAND +#define POLLRDBAND 0 +#endif +#ifndef POLLWRNORM +#define POLLWRNORM 0 +#endif +#ifndef POLLWRBAND +#define POLLWRBAND 0 +#endif + +#define SELECT_R_IN (POLLIN | POLLRDNORM | POLLRDBAND) +#define SELECT_W_IN (POLLOUT | POLLWRNORM | POLLWRBAND) +#define SELECT_X_IN (POLLPRI) + +#define SELECT_R_OUT (SELECT_R_IN | POLLERR | POLLHUP) +#define SELECT_W_OUT (SELECT_W_IN | POLLERR) +#define SELECT_X_OUT (SELECT_X_IN) + +void pollwrap_add_fd_rwx(pollwrapper *pw, int fd, int rwx) +{ + int events = 0; + if (rwx & SELECT_R) + events |= SELECT_R_IN; + if (rwx & SELECT_W) + events |= SELECT_W_IN; + if (rwx & SELECT_X) + events |= SELECT_X_IN; + pollwrap_add_fd_events(pw, fd, events); +} + +int pollwrap_poll_instant(pollwrapper *pw) +{ + return poll(pw->fds, pw->nfd, 0); +} + +int pollwrap_poll_endless(pollwrapper *pw) +{ + return poll(pw->fds, pw->nfd, -1); +} + +int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds) +{ + assert(milliseconds >= 0); + return poll(pw->fds, pw->nfd, milliseconds); +} + +static void pollwrap_get_fd_events_revents(pollwrapper *pw, int fd, + int *events_p, int *revents_p) +{ + pollwrap_fdtopos *f2p, f2p_find; + int events = 0, revents = 0; + + assert(fd >= 0); + + f2p_find.fd = fd; + f2p = find234(pw->fdtopos, &f2p_find, NULL); + if (f2p) { + events = pw->fds[f2p->pos].events; + revents = pw->fds[f2p->pos].revents; + } + + if (events_p) + *events_p = events; + if (revents_p) + *revents_p = revents; +} + +int pollwrap_get_fd_events(pollwrapper *pw, int fd) +{ + int revents; + pollwrap_get_fd_events_revents(pw, fd, NULL, &revents); + return revents; +} + +int pollwrap_get_fd_rwx(pollwrapper *pw, int fd) +{ + int events, revents; + pollwrap_get_fd_events_revents(pw, fd, &events, &revents); + int rwx = 0; + if ((events & POLLIN) && (revents & SELECT_R_OUT)) + rwx |= SELECT_R; + if ((events & POLLOUT) && (revents & SELECT_W_OUT)) + rwx |= SELECT_W; + if ((events & POLLPRI) && (revents & SELECT_X_OUT)) + rwx |= SELECT_X; + return rwx; +} |