3

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.

pattivacek
  • 5,617
  • 5
  • 48
  • 62
  • 3
    Did you `strace` your program? Did you check return of [nanosleep(2)](http://man7.org/linux/man-pages/man2/nanosleep.2.html) and used `errno` (e.g. thru `perror`) on failure. What kernel do you run? I am a bit sceptical: on all my applications, `nanosleep` is working as documented. Please show some actual code! – Basile Starynkevitch Sep 26 '14 at 16:11
  • You might like to show us how you call `nanosleep()` and how you initialise the strcutures you pass into? – alk Sep 26 '14 at 16:20
  • See new edit. Sorry, I should have explained the return value in my original post. – pattivacek Sep 26 '14 at 16:20
  • 4
    To me this looks as if your a missing 1000 some where: [ms]%1000=[ms] and [ms]*1000=[us] but you want [ns], so this should be `...tv_nsec = ((long)(milliseconds%1000)) * 1000 * 1000`, shouldn't it? – alk Sep 26 '14 at 16:24
  • Where is `usec` declared? – chux - Reinstate Monica Sep 26 '14 at 16:24
  • I woudl expect `usec = (*milliseconds) * 1000;` and not `usec = (*milliseconds) % 1000;` – chux - Reinstate Monica Sep 26 '14 at 16:27
  • @chux this would overflow nsec in terms of that it might become larger then 10**-9 - 1secs. – alk Sep 26 '14 at 16:28
  • Why pass the milliseconds as a pointer — why not just a value. There are a million nanoseconds in a millisecond, so your sub-second delays are 1/1000th of what you want. – Jonathan Leffler Sep 26 '14 at 16:30
  • @alk, that was it! I can't believe I never noticed that before. That typo has existed in our code for at least five years... Anyway, if you make it an answer, I'll accept it. – pattivacek Sep 26 '14 at 16:30
  • @alk All depends on the range of `int`, `long`. IAC, using a right sized type, I would still expect `usec = (*milliseconds) * 1000`. – chux - Reinstate Monica Sep 26 '14 at 16:31
  • @JonathanLeffler, the pointer was because this is used in a compatibility routine called from Fortran, where the default (with `iso_c_binding` is to pass everything by reference. Obscure and unnecessary for the big picture, but that's where it came from. – pattivacek Sep 26 '14 at 16:32
  • @patrickvacek: fair enough — that's a perfectly good, though non-obvious, reason for the interface shown. And it probably was not necessary to mention that in the original version of the question, though that's slightly borderline. – Jonathan Leffler Sep 26 '14 at 16:35
  • Thanks, all! That was fast. My only defense is that I didn't write the original code, although I have no explanation for how I didn't catch the glaring math error upon review of the code in light of the bug. – pattivacek Sep 26 '14 at 16:42
  • @chux: Regarding my use of "*overflow*", from `nanosleep()`'s (linux) man-page: `The value of the nanoseconds field must be in the range 0 to 999999999.` – alk Sep 26 '14 at 16:46
  • @alk Regari`ngs me use of _"usec"_, which I was not assigning to any field, was simply that to convert from msec to usec, code should `* 1000` rather than `% 1000` which is what your did in your fine answer. Your answer used well-named variables like `ms_remaining` to convey the proper meaning of code's intent. – chux - Reinstate Monica Sep 26 '14 at 16:55

3 Answers3

8

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.

alk
  • 69,737
  • 10
  • 105
  • 255
  • BTW: Are the order of Posix structures fixed? – chux - Reinstate Monica Sep 26 '14 at 16:59
  • 1
    @chux: Which kind of "*order*" are you referring to, please? – alk Sep 27 '14 at 06:51
  • 1
    `struct timespec ts_sleep = { *pmilliseconds / 1000, (*pmilliseconds % 1000) * 1000000L };` only works if the order of the fields are fixed. E.g. `div_t` for C compliant code can have its two fields in either order. So does Posix fix the field order of the structure? – chux - Reinstate Monica Sep 27 '14 at 14:16
  • 1
    @chux: Interesting question - up until now I assumed "yes", the order is well defined. After having read here: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/time.h.html I'm not so sure anymore. You might like to pose this question at SO, though! ;-) – alk Sep 28 '14 at 10:07
  • The third source code block you have ``while (EINTR == result);`` which should be ``while (result == -1 && errno == EINTR);`` – carveone Oct 19 '18 at 14:19
  • @carveone: Oops, indeed! Thank you for notifying. Fixed. – alk Oct 19 '18 at 16:11
0

I did it like below and it worked...

#include <stdio.h>
#include <time.h>   /* Needed for struct timespec */


int nsleep(long miliseconds)
{
   struct timespec req, rem;

   if(miliseconds > 999)
   {   
        req.tv_sec = (int)(miliseconds / 1000);                            /* Must be Non-Negative */
        req.tv_nsec = (miliseconds - ((long)req.tv_sec * 1000)) * 1000000; /* Must be in range of 0 to 999999999 */
   }   
   else
   {   
        req.tv_sec = 0;                         /* Must be Non-Negative */
        req.tv_nsec = miliseconds * 1000000;    /* Must be in range of 0 to 999999999 */
   }   

   return nanosleep(&req , &rem);
}

int main()
{
   int ret = nsleep(2500);
   printf("sleep result %d\n",ret);
   return 0;
}
Sunny Shukla
  • 537
  • 1
  • 6
  • 17
0

Here is the method

  static void Sleep(long lMs){
       //Calculate the nanosecond
       long lRemainingMilliSecond = (lMs) % 1000;
       long lNanoSecond = lRemainingMilliSecond * 1000000;

       struct timespec ts_sleep,ts_remaining;

       ts_sleep.tv_sec = (lMs) / 1000;
       ts_sleep.tv_nsec = lNanoSecond;
       nanosleep(&ts_sleep, &ts_remaining);
}

The concept is explained better in the following page Convert milliseconds to timespec - GNU Porting

Community
  • 1
  • 1
jfk
  • 4,335
  • 34
  • 27