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.
This commit is contained in:
Xavier Del Campo Romero 2023-04-28 00:19:32 +02:00
parent fa997aa2c1
commit dc063b90cb
Signed by: xavi
GPG Key ID: 84FF3612A9BF43F2
3 changed files with 70 additions and 31 deletions

View File

@ -233,7 +233,7 @@ int handler_listen(struct handler *const h, const short port)
for (;;) for (;;)
{ {
bool exit, io; 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) if (exit)
{ {
@ -242,7 +242,7 @@ int handler_listen(struct handler *const h, const short port)
} }
else if (!c) else if (!c)
{ {
fprintf(stderr, "%s: server_select failed\n", __func__); fprintf(stderr, "%s: server_poll failed\n", __func__);
return -1; return -1;
} }

View File

@ -2,9 +2,9 @@
#include "server.h" #include "server.h"
#include <fcntl.h> #include <fcntl.h>
#include <sys/select.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <poll.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
@ -170,63 +170,102 @@ static void handle_signal(const int signum)
do_exit = 1; 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) bool *const exit)
{ {
int nfds = -1; struct server_client *ret = NULL;
fd_set rfds, wfds; 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; *io = *exit = false;
*sfd = (const struct pollfd)
FD_ZERO(&rfds);
FD_ZERO(&wfds);
for (size_t i = 0; i < s->n; i++)
{ {
.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 struct server_client *const c = &s->c[i];
const int fd = c->fd; const int fd = c->fd;
FD_SET(fd, &rfds); *p = (const struct pollfd)
{
.fd = fd,
.events = POLLIN
};
if (c->write) if (c->write)
FD_SET(fd, &wfds); p->events |= POLLOUT;
if (fd > nfds)
nfds = fd;
} }
FD_SET(s->fd, &rfds); int res;
if (s->fd > nfds) again:
nfds = s->fd;
const int res = select(nfds + 1, &rfds, &wfds, NULL, NULL); res = poll(fds, n, -1);
if (res < 0) if (res < 0)
{ {
if (do_exit) if (do_exit)
{
*exit = true; *exit = true;
else goto end;
fprintf(stderr, "%s: select(2): %s\n", __func__, strerror(errno)); }
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)) else if (!res)
return alloc_client(s);
for (size_t i = 0; i < s->n; i++)
{ {
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]; struct server_client *const c = &s->c[i];
if (FD_ISSET(c->fd, &rfds) || FD_ISSET(c->fd, &wfds)) if (p->revents)
{ {
*io = true; *io = true;
return c; ret = c;
goto end;
} }
} }
fprintf(stderr, "%s: unlisted fd\n", __func__); fprintf(stderr, "%s: unlisted fd\n", __func__);
return NULL;
end:
free(fds);
return ret;
} }
static int init_signals(void) static int init_signals(void)

View File

@ -5,7 +5,7 @@
#include <stddef.h> #include <stddef.h>
struct server *server_init(unsigned short port); 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_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_write(const void *buf, size_t n, struct server_client *c);
int server_close(struct server *s); int server_close(struct server *s);