user1986698
user1986698

Reputation: 33

pselect gets interrupted occationally

First and formost, I didn't have the reputation to tag this as being a question about 'pselect', so I went with 'select'.

I use pselect to handle timeouts on an UDP socket. The code looks as follows:

UDP_STATUS udp_socket_recv(udp_socket_t* p_sock, int* p_bytes_rcvd)
{
    int res = 0;
    fd_set fds;
    struct timespec timeout;

    FD_ZERO(&fds);
    FD_SET(p_sock->m_socket, &fds);

    if (p_sock->m_timeout == NULL) {
        res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, NULL, NULL);
    } else {
        timeout.tv_sec = p_sock->m_timeout->tv_sec;
        timeout.tv_msec = p_sock->m_timeout->tv_usec * 1000;
        res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, &timeout, NULL);
    }

    if (res == 0)
        return UDP_TIMEOUT;
    else if (res == -1) {
        printf("pselect error: %s\n", strerror(errno)); /* Sometimes we end up here */
        return UDP_FAILURE;
    }

    res = recvfrom(p_sock->m_socket, ..); /* etc etc */
}

Now, the above works just fine in the vast majority of cases (although I might have misstyped something as I don't have access to copy/paste). pselect does however sometimes return -1, with the strerror(errno) call printing "Interrupted system call".

I'm not even sure this is how you want to do socket timeouts, I can't even remember how I came up with this solution...

Any help is much appreciated.

Upvotes: 0

Views: 1074

Answers (2)

wildplasser
wildplasser

Reputation: 44250

The EINTR / interrupted system call is not an error condition, just something that happened (a signal was delivered and maybe handled) while your program whas blocked inside the system call. You can ignore it and simply loop, like here: (the below program is not optimal, only a demonstration of how you can handle the EINTR)

UDP_STATUS udp_socket_recv(udp_socket_t* p_sock, int* p_bytes_rcvd)
{
int res = 0;
fd_set fds;
struct timespec timeout;

FD_ZERO(&fds);
FD_SET(p_sock->m_socket, &fds);

while(1) {
  if (p_sock->m_timeout == NULL) {
      res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, NULL, NULL);
  } else {
      timeout.tv_sec = p_sock->m_timeout->tv_sec;
      timeout.tv_msec = p_sock->m_timeout->tv_usec * 1000;
      res = pselect(p_sock->m_socket + 1, &fds, NULL, NULL, &timeout, NULL);
  }

  if (res > 0) break;
  if (res == 0)
    return UDP_TIMEOUT;

  switch(errno) {
  case EINTR: continue;
  default:
    printf("pselect error: %s\n", strerror(errno)); /* Sometimes we end up here */
    return UDP_FAILURE;
   }
 }

res = recvfrom(p_sock->m_socket, ..); /* etc etc */
}

Upvotes: 2

tmyklebu
tmyklebu

Reputation: 14215

No, really, just restart the system call. If it fails again, that means your process received another signal. EINTR, as far as I'm aware, is largely an implementation artefact from early Unix systems so that they could deliver signals while blocking system calls were in progress. Lots of ink has been spilled on whether it was a smart or a stupid design decision, but the upshot of it all is that code making blocking system calls needs to retry when the calls fail with EINTR.

Upvotes: 0

Related Questions