8.10 nanosleep: High-Precision Sleeping

The nanosleep system call is a high-precision version of the standard UNIX sleep call. Instead of sleeping an integral number of seconds, nanosleep takes as its argument a pointer to a struct timespec object, which can express time to nanosecond precision. However, because of the details of how the Linux kernel works, the actual precision provided by nanosleep is 10 milliseconds—still better than that afforded by sleep. This additional precision can be useful, for instance, to schedule frequent operations with short time intervals between them.

The struct timespec structure has two fields: tv_sec, the integral number of seconds, and tv_nsec, an additional number of milliseconds. The value of tv_nsec must be less than 109.

The nanosleep call provides another advantage over sleep. As with sleep, the delivery of a signal interrupts the execution of nanosleep, which sets errno to EINTR and returns -1. However, nanosleep takes a second argument, another pointer to a struct timespec object, which, if not null, is filled with the amount of time remaining (that is, the difference between the requested sleep time and the actual sleep time). This makes it easy to resume the sleep operation.

The function in Listing 8.8 provides an alternate implementation of sleep. Unlike the ordinary system call, this function takes a floating-point value for the number of seconds to sleep and restarts the sleep operation if it's interrupted by a signal.

Listing 8.8 (better_sleep.c) High-Precision Sleep Function
#include <errno.h> 
#include <time.h> 
 
int better_sleep (double sleep_time) 
{
  struct timespec tv; 
  /* Construct the timespec from the number of whole seconds...  */ 
  tv.tv_sec = (time_t) sleep_time; 
  /* ... and the remainder in nanoseconds.  */ 
  tv.tv_nsec = (long) ((sleep_time - tv.tv_sec) * 1e+9); 
 
  while (1) 
  {
    /* Sleep for the time specified in tv. If interrupted by a 
       signal, place the remaining time left to sleep back into tv.  */ 
    int rval = nanosleep (&tv, &tv); 
    if (rval == 0) 
      /* Completed the entire sleep time; all done.  */ 
      return 0; 
    else if (errno == EINTR) 
      /* Interrupted by a signal. Try again.  */ 
      continue; 
    else 
      /* Some other error; bail out.  */ 
      return rval; 
  } 
  return 0; 
}