[Refresher] I/O Multiplexing with select()

Last Updated: 12-Nov-08

We want to make a Unix system call select() and proceed only when

  • some file descriptors are ready for reading, or
  • some file descriptors are ready for writing, or
  • after 2.3 seconds (maximum) have elapsed.
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

...
fd_set rset, wset;  // Declare the descriptor sets
int maxfdp;
struct timeval timeout;
int retval;

for (;;) {
  /* Clear the descriptor sets */
  FD_ZERO(&rset);  // Clear all bits
  FD_ZERO(&wset);

  /* The descriptor sets */
  FD_SET(fd_read1, &rset);  // Set the bit
  FD_SET(fd_read2, &rset);
  ...
  FD_SET(fd_write1, &wset);
  ...
  // FD_CLR(fd, &wset);   // Clear the bit for fd
  ...
  maxfdp = fd_read1;
  if (fd_read2 > maxfdp)
    maxfdp = fd_read2;
  ...

  /* Timeout: 2.3 seconds */
  /* Always set 'timeout' before select() since it might have been updated by the previous select() call
   * The select() of some Unix systems (such as Linux) updates the 'timeout' argument to indicate how
   * much time was left. (POSIX.1-2001 does not enforce but permits this behavior.)
   */
  timeout.tv_sec = 2;     // seconds
  timeout.tv_usec = 300000;  // microseconds; 1 microsecond = 1E-6 second

  retval = select(maxfdp + 1,
                  &rset,  // Ready for reading?  Can be NULL.
                  &wset,  // Ready for writing?  Can be NULL.
                  NULL,   // Have an exception condition pending?  Can be NULL.
                  &timeout  // NULL for not setting the timeout
                  );

  if (retval > 0) {  // Positive counts of ready descriptors
    if (FD_ISSET(fd_read1, &rset)) {
      // fd_read1 is readable
     ...
    }
    if (FD_ISSET(fd_read2, &rset)) {
      // fd_read2 is readable
     ...
    }
    if (FD_ISSET(fd_write1, &wset)) {
      // fd_write1 is writable
     ...
    }
    ...
    // NEVER check the file descriptors not included in the sets for select(),
    // or you may get Segmentation Fault.
    ...
  }
  else if (retval == 0) {  // Timeout
    ...
  }
  else {  // retval < 0
    // Error.  Can refer to errno for the exact reason.
    if (errno == EINTR) {
      // The call was interrupted as a signal was caught. Should retry.
    }
    ...
  }
}
...