patrickvacek patrickvacek -4 years ago 163
C Question

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.

Answer Source

You are missing a factor 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 interupted 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 == result);
  }

  if (result)
  {
    perror("nanosleep() failed");
    result = -1;
  }

  return result;
}

Followining a wrapper to fulfill 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("msleep() 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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download