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:
parent
fa997aa2c1
commit
dc063b90cb
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
95
server.c
95
server.c
|
@ -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)
|
||||||
|
|
2
server.h
2
server.h
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue