aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-04-28 00:19:32 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-07-20 23:52:53 +0200
commit2e8485d687dd5f673b8d2aadb0816571d3f32cb7 (patch)
tree6319effdcacf839e8f2a899565cf6460b32c08ec
parent82fffd1acebf688f1c082d7a597d68239e8a39cc (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.c4
-rw-r--r--server.c91
-rw-r--r--server.h2
3 files changed, 68 insertions, 29 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 <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)
diff --git a/server.h b/server.h
index 74189db..74f06ae 100644
--- a/server.h
+++ b/server.h
@@ -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);