TIL: Use poll to handle I/O multiplexing across multiple Unix sockets
2022-06-08 00:00:00 +0000 UTCpoll() is a key Unix syscall that allows the caller to wait on change to a file descriptor. Here is the signature and relevant type definitions:
typedef unsigned long int nfds_t;
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
To prepare the array of watched file descriptors, we set the fd and events members of the pollfd instances. fd is just the integer file descriptor. events is a bitmasked combination of the event types we want poll to watch for. This can include many options detailed in man 2 poll. Two are POLLIN and POLLOUT, indicating that there is data to be read or that the FD is ready to be written to respectively.
//...
struct pollfd* fds = 0;
fds = malloc(sizeof(struct pollfd) * MAX_FDS); // set MAX_FDS elsewhere
nfds_t nfds = 1;
fd[0].fd = socket_descriptor; // socket descriptor obtained earlier in execution
fd[0].events = POLLIN | POLLOUT; // notify us if the socket has data to read _or_ if the socket is ready to be written to
//...
Once we have that array set up, we can call poll() and handle the results. Poll’s return value is the nonnegative number of elements in the fds array whose revents field has been set. It is 0 if the timeout is reached. It is negative in the case of an error. The timeout argument is denominated in milliseconds. So we can work with poll like this:
//...
int npoll = poll(fds, nfds, 500);
if (npoll < 0) {
perror("poll");
exit(0);
} else if (npoll > 0) {
size_t fds_touched = 0;
for (size_t i = 0; i < nfds && fds_touched < npoll; i++) {
if (fds[i].revents) {
// if revents is non-zero, this FD has something for us to do
// you can also check conditions like (fd[i].revents & POLLIN)
// to check for sockets that are ready to read vs write vs error states
fds_touched++;
}
}
} else {
// do something you want to do if no FDs are ready
}
In a practical context this would likely be wrapped in a loop.
Other resources: