Search code examples
clinuxtimerposix-select

updating time out values in select() calls


I want to implement a timer using select() and I want it to reach the timeout code after intervals of every 3 seconds.

if I remove "timeout.tv_sec+=8;" from my code it waits for 3 seconds and then starts printing "time out" continuously and with "timeout.tv_sec+=8" nothing gets printed, the program gets stuck. Nothing gets printed. Can someone explain me what I am doing wrong in this? My code is as follows : Basically, I want it to print time out at an interval of 3 seconds.

struct timeval timeout;
timeout.tv_sec = 3;
while(1) {
  int rc = select(2, NULL, NULL, NULL, &timeout);
  if(rc == 0)
  {
      printf("  time out  "); 
      timeout.tv_sec+=8;
  }      
  if (rc < 0)
  {
      printf("  Error  ");
      continue;
  }
  else
  {
     printf("  process  ");// some instructions
  }
}

Solution

  • You have a combination of two problems.

    • on Linux, the time out struct is modified by select to indicate how much time is left
    • printf uses buffered IO. Text written using printf remains in a buffer in the program until either \n is printed or fflush is called on stdout or the buffer is full.

    If you omit timeout.tv_sec+=8, your code waits 3 seconds, then prints time out except it only goes to an internal buffer in the program. The next time round the loop, the time out is now zero, so the program prints time out to the buffer straight away. The same for any subsequent executions of the loop. Fairly quickly the buffer gets filled up and the program flushes it to stdout at which point you will see it on the screen.

    If you leave in timeout.tv_sec+=8 on the first iteration the select waits 3 seconds, on the second and each subsequent iteration, it waits 8 seconds. This means it takes much longer to fill up the buffer, so it will be much longer before you see any output. You print 10 chars. If the internal buffer is 4096 bytes, it will take about 55 minutes to print anything at all.

    To fix it, either put a \n on each print statement, or fflush stdout regularly.

    There is one other thing you need to do and that is to set timeout.tv_usec to 0 each time you set timeout.tv_sec. Your code, as it stands leaves half the struct uninitialised and therefore invokes undefined behaviour.

    struct timeval timeout;
    while(1) {
      timeout.tv_sec = 3 ;
      timeout.tv_usec = 0; // Otherwise UB!
      int rc = select(2, NULL, NULL, NULL, &timeout);
      if(rc == 0)
      {
          printf("  time out  "); 
      }      
      if (rc < 0)
      {
          printf("  Error  ");
          break; // The error is unlikely to go away next time around
      }
      else
      {
         printf("  process  ");// some instructions
      }
    }