Search code examples
clinuxsleep

nanosleep does not work for values less than a second


I have a program (mixed C and Fortran, although that doesn't seem to be relevant) that uses nanosleep. However, if my timespec has a tv_sec value of 0, it simply doesn't sleep. The tv_nsec value can be microseconds shy of a full second, but it does not sleep. (If tv_sec is 1, it has no problem sleeping for a second.) Why would this be?

To make things more confusing, usleep with an appropriate value (i.e. 995000 usec) sleeps for just about a second as expected.

I'm seeing this problem with a RHEL 5.8 and a RHEL 6.4 box. Both are using gcc.

Here's the function that calls nanosleep:

void msleep(int *milliseconds)
{
    long usec;
    struct timespec sleep;
    usec = (*milliseconds) % 1000;
    sleep.tv_sec = (*milliseconds) / 1000;
    sleep.tv_nsec = 1000*usec;
    nanosleep(&sleep, NULL);
}

Obviously, I don't actually need nanosecond precision!

I've also tested a version in which I did check the return value; it was always 0 (success), and thus the rem output parameter (remaining time if interrupted) never got set.


Solution

  • You are missing a factor of 1000.

    Try this:

    #define _POSIX_C_SOURCE 199309L /* shall be >= 199309L */
    
    #include <time.h>
    
    void msleep(int *milliseconds)  
    {
      int ms_remaining = (*milliseconds) % 1000;
      long usec = ms_remaining * 1000;
      struct timespec ts_sleep;
    
      ts_sleep.tv_sec = (*milliseconds) / 1000;
      ts_sleep.tv_nsec = 1000*usec;
      nanosleep(&ts_sleep, NULL);
    }
    

    More compact:

    #define _POSIX_C_SOURCE 199309L /* shall be >= 199309L */
    
    #include <time.h>
    
    void msleep(int * pmilliseconds)  
    {
      struct timespec ts_sleep = 
      {
        *pmilliseconds / 1000,
        (*pmilliseconds % 1000) * 1000000L
      };
    
      nanosleep(&ts_sleep, NULL);
    }
    

    Finally a complete implementation including error handling and the case of nanosleep() being interrupted early:

    #define _POSIX_C_SOURCE 199309L
    
    #include <time.h>
    #include <errno.h>
    #include <stdio.h>
    
    int ms_sleep(unsigned int ms)
    {
      int result = 0;
    
      {
        struct timespec ts_remaining =
        { 
          ms / 1000, 
          (ms % 1000) * 1000000L 
        };
    
        do
        {
          struct timespec ts_sleep = ts_remaining;
          result = nanosleep(&ts_sleep, &ts_remaining);
        } 
        while ((EINTR == errno) && (-1 == result));
      }
    
      if (-1 == result)
      {
        perror("nanosleep() failed");
      }
    
      return result;
    }
    

    Following a wrapper to fulfil the OP's requirements:

    #include <errno.h>
    #include <stdio.h>
    
    int ms_sleep(unsigned int);
    
    void msleep(int * pms)
    {
      int result = 0;
    
      if ((NULL == pms) || (0 > *pms)) /* Check for valid input. */
      {
        errno = EINVAL;
        result = -1;
      }
      else 
      {
        result = ms_sleep(*pms));
      }
    
      if (-1 == result)
      {
        perror("ms_sleep() failed");
        /* Exit and/or log error here. */
      }
    }
    

    Update (referring to chux's comment below):

    Assuming at least C99, this part of the above code

      struct timespec ts_sleep = 
      {
        *pmilliseconds / 1000,
        (*pmilliseconds % 1000) * 1000000L
      };
    

    might better be written like this

      struct timespec ts_sleep = 
      {
        .tv_sec = *pmilliseconds / 1000,
        .tv_nsec = (*pmilliseconds % 1000) * 1000000L
      };
    

    to not rely on the order of struct timespec's members.