Non-blocking connect() and EINTR

2k Views Asked by At

I am using connect_nonb() from Stevens, UNIX Network programming:

int
connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec)
{
    int                     flags, n, error;
    socklen_t               len;
    fd_set                  rset, wset;
    struct timeval  tval;

    flags = Fcntl(sockfd, F_GETFL, 0);
    Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

    error = 0;
    if ( (n = connect(sockfd, saptr, salen)) < 0)
            if (errno != EINPROGRESS)
                    return(-1);

    /* Do whatever we want while the connect is taking place. */

    if (n == 0)
            goto done;      /* connect completed immediately */

    FD_ZERO(&rset);
    FD_SET(sockfd, &rset);
    wset = rset;
    tval.tv_sec = nsec;
    tval.tv_usec = 0;

    if ( (n = Select(sockfd+1, &rset, &wset, NULL,
                                     nsec ? &tval : NULL)) == 0) {
            close(sockfd);          /* timeout */
            errno = ETIMEDOUT;
            return(-1);
    }

    if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
            len = sizeof(error);
            if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
                    return(-1);                     /* Solaris pending error */
    } else
            err_quit("select error: sockfd not set");

done:
    Fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */

    if (error) {
            close(sockfd);          /* just in case */
            errno = error;
            return(-1);
    }
    return(0);
}

This function allows a custom timeout of connect(). If, whilst blocking in select() waiting for the connect to succeed, a signal is received, select() exits with -1 (EINTR). At this point the select() timeout has not expired, the connect has not succeeded (i.e. the target host could be disconnected) but the subsequent getsockopt() does not return an error.

Should getsockopt() return an error or should the Stevens code check the return code (and errno) of select()?

Currently when connecting to a non-existent host and a signal interrupts select() this function returns success incorrectly.

1

There are 1 best solutions below

0
Celada On

I'm not sure what Select() is. I assume it's some kind of thin wrapper around select().

In most applications, whenever select() fails with EINTR, you should silently loop and call select() again, possibly after recalculating the timeout to account for the fact that some time has elapsed in the prior call to select().

This case is no exception. select() should be in a loop.