[ Team LiB ] Previous Section Next Section

6.9 pselect Function

The pselect function was invented by POSIX and is now supported by many of the Unix variants.

#include <sys/select.h>

#include <signal.h>

#include <time.h>

int pselect (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timespec *timeout, const sigset_t *sigmask);

Returns: count of ready descriptors, 0 on timeout, –1 on error

pselect contains two changes from the normal select function:

  1. pselect uses the timespec structure, another POSIX invention, instead of the timeval structure.

    
    
    struct timespec {
      time_t tv_sec;       /* seconds */
      long   tv_nsec;      /* nanoseconds */
    };
    

    The difference in these two structures is with the second member: The tv_nsec member of the newer structure specifies nanoseconds, whereas the tv_usec member of the older structure specifies microseconds.

  2. pselect adds a sixth argument: a pointer to a signal mask. This allows the program to disable the delivery of certain signals, test some global variables that are set by the handlers for these now-disabled signals, and then call pselect, telling it to reset the signal mask.

With regard to the second point, consider the following example (discussed on pp. 308–309 of APUE). Our program's signal handler for SIGINT just sets the global intr_flag and returns. If our process is blocked in a call to select, the return from the signal handler causes the function to return with errno set to EINTR. But when select is called, the code looks like the following:


if (intr_flag)
    handle_intr();       /* handle the signal */
if ( (nready = select( ... )) < 0) {
    if (errno == EINTR) {
        if (intr_flag)
            handle_intr();
    }
    ...
}

The problem is that between the test of intr_flag and the call to select, if the signal occurs, it will be lost if select blocks forever. With pselect, we can now code this example reliably as


sigset_t newmask, oldmask, zeromask;

sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);

sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */
if (intr_flag)
    handle_intr();     /* handle the signal */
if ( (nready = pselect ( ... , &zeromask)) < 0) {
    if (errno == EINTR)  {
        if (intr_flag)
            handle_intr ();
    }
    ...
}

Before testing the intr_flag variable, we block SIGINT. When pselect is called, it replaces the signal mask of the process with an empty set (i.e., zeromask) and then checks the descriptors, possibly going to sleep. But when pselect returns, the signal mask of the process is reset to its value before pselect was called (i.e., SIGINT is blocked).

We will say more about pselect and show an example of it in Section 20.5. We will use pselect in Figure 20.7 and show a simple, albeit incorrect, implementation of pselect in Figure 20.8.

There is one other slight difference between the two select functions. The first member of the timeval structure is a signed long integer, while the first member of the timespec structure is a time_t. The signed long in the former should also be a time_t, but was not changed retroactively to avoid breaking existing code. The brand new function, however, could make this change.

    [ Team LiB ] Previous Section Next Section