From dc063b90cbf13a463c29ee5b81abeb07b6698473 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Fri, 28 Apr 2023 00:19:32 +0200 Subject: [PATCH] Replace select(2) with poll(2) select(2) has a number of well-known issues (e.g.: FD_SETSIZE limiting the maximum amount of file descriptors to watch) that are mostly solved by poll(2) and thus can be used as a drop-in replacement. --- handler.c | 4 +-- server.c | 95 +++++++++++++++++++++++++++++++++++++++---------------- server.h | 2 +- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/handler.c b/handler.c index 73ac9ab..795d503 100644 --- a/handler.c +++ b/handler.c @@ -233,7 +233,7 @@ int handler_listen(struct handler *const h, const short port) for (;;) { bool exit, io; - struct server_client *const c = server_select(h->server, &io, &exit); + struct server_client *const c = server_poll(h->server, &io, &exit); if (exit) { @@ -242,7 +242,7 @@ int handler_listen(struct handler *const h, const short port) } else if (!c) { - fprintf(stderr, "%s: server_select failed\n", __func__); + fprintf(stderr, "%s: server_poll failed\n", __func__); return -1; } diff --git a/server.c b/server.c index f740384..a561bfb 100644 --- a/server.c +++ b/server.c @@ -2,9 +2,9 @@ #include "server.h" #include -#include #include #include +#include #include #include #include @@ -170,63 +170,102 @@ static void handle_signal(const int signum) do_exit = 1; } -struct server_client *server_select(struct server *const s, bool *const io, +struct server_client *server_poll(struct server *const s, bool *const io, bool *const exit) { - int nfds = -1; - fd_set rfds, wfds; + struct server_client *ret = NULL; + const nfds_t n = s->n + 1; + struct pollfd *const fds = malloc(n * sizeof *fds); + + if (!fds) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto end; + } + + struct pollfd *const sfd = &fds[0]; *io = *exit = false; - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - - for (size_t i = 0; i < s->n; i++) + *sfd = (const struct pollfd) { + .fd = s->fd, + .events = POLLIN + }; + + for (size_t i = 0, j = 1; i < s->n; i++, j++) + { + struct pollfd *const p = &fds[j]; const struct server_client *const c = &s->c[i]; const int fd = c->fd; - FD_SET(fd, &rfds); + *p = (const struct pollfd) + { + .fd = fd, + .events = POLLIN + }; if (c->write) - FD_SET(fd, &wfds); - - if (fd > nfds) - nfds = fd; + p->events |= POLLOUT; } - FD_SET(s->fd, &rfds); + int res; - if (s->fd > nfds) - nfds = s->fd; +again: - const int res = select(nfds + 1, &rfds, &wfds, NULL, NULL); + res = poll(fds, n, -1); if (res < 0) { if (do_exit) + { *exit = true; - else - fprintf(stderr, "%s: select(2): %s\n", __func__, strerror(errno)); + goto end; + } - return NULL; + switch (errno) + { + case EAGAIN: + /* Fall through. */ + case EINTR: + goto again; + + default: + fprintf(stderr, "%s: poll(2): %s\n", __func__, strerror(errno)); + break; + } + + goto end; } - else if (FD_ISSET(s->fd, &rfds)) - return alloc_client(s); - - for (size_t i = 0; i < s->n; i++) + else if (!res) { + fprintf(stderr, "%s: poll(2) returned zero\n", __func__); + goto end; + } + + if (sfd->revents) + { + ret = alloc_client(s); + goto end; + } + + for (size_t i = 0, j = 1; i < s->n; i++, j++) + { + const struct pollfd *const p = &fds[j]; struct server_client *const c = &s->c[i]; - if (FD_ISSET(c->fd, &rfds) || FD_ISSET(c->fd, &wfds)) + if (p->revents) { *io = true; - return c; + ret = c; + goto end; } } fprintf(stderr, "%s: unlisted fd\n", __func__); - return NULL; + +end: + free(fds); + return ret; } static int init_signals(void) diff --git a/server.h b/server.h index 74189db..74f06ae 100644 --- a/server.h +++ b/server.h @@ -5,7 +5,7 @@ #include struct server *server_init(unsigned short port); -struct server_client *server_select(struct server *s, bool *io, bool *exit); +struct server_client *server_poll(struct server *s, bool *io, bool *exit); int server_read(void *buf, size_t n, struct server_client *c); int server_write(const void *buf, size_t n, struct server_client *c); int server_close(struct server *s);