Search code examples
csocketsudpposix-select

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.


Solution

  • 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 */
    }