diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-04-28 00:19:32 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-07-20 23:52:53 +0200 |
| commit | 2e8485d687dd5f673b8d2aadb0816571d3f32cb7 (patch) | |
| tree | 6319effdcacf839e8f2a899565cf6460b32c08ec | |
| parent | 82fffd1acebf688f1c082d7a597d68239e8a39cc (diff) | |
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.
| -rw-r--r-- | handler.c | 4 | ||||
| -rw-r--r-- | server.c | 91 | ||||
| -rw-r--r-- | server.h | 2 |
3 files changed, 68 insertions, 29 deletions
@@ -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; } @@ -2,9 +2,9 @@ #include "server.h" #include <fcntl.h> -#include <sys/select.h> #include <sys/socket.h> #include <netinet/in.h> +#include <poll.h> #include <unistd.h> #include <errno.h> #include <signal.h> @@ -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); - *io = *exit = false; + if (!fds) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto end; + } - FD_ZERO(&rfds); - FD_ZERO(&wfds); + struct pollfd *const sfd = &fds[0]; - for (size_t i = 0; i < s->n; i++) + *io = *exit = false; + *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 (!res) + { + fprintf(stderr, "%s: poll(2) returned zero\n", __func__); + goto end; } - else if (FD_ISSET(s->fd, &rfds)) - return alloc_client(s); - for (size_t i = 0; i < s->n; i++) + 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) @@ -5,7 +5,7 @@ #include <stddef.h> 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); |
