We don’t have millisecond level control in the sleep() function. But in Linux we can implement our own function that will take duration in milliseconds as input and sleep for that duration.
Using usleep()
The usleep() function takes the duration in micro seconds as input. We can have a wrapper around this usleep() function that will take the duration in millisecond. We can simply pass that duration multiplied by 1000 to usleep() function.
#include <unistd.h>
int msleep(unsigned int tms) {
return usleep(tms * 1000);
}
This is good enough solution. But thing is that the usleep() function is now deprecated. We have few other options also.
Using nanosleep()
We can implement our msleep() function using nanosleep() that takes timespec structure as input. It has nano second level control. We can convert the input milliseconds into seconds and nanoseconds before calling the nanosleep() function.
#include <errno.h>
#include <time.h>
int msleep(long tms)
{
struct timespec ts;
int ret;
if (tms < 0)
{
errno = EINVAL;
return -1;
}
ts.tv_sec = tms / 1000;
ts.tv_nsec = (tms % 1000) * 1000000;
do {
ret = nanosleep(&ts, &ts);
} while (ret && errno == EINTR);
return ret;
}
You might notice that there is a loop around nanosleep() function. To understand this we need to understand the behavior of nanosleep() function.
From the nanospleep() man page.
nanosleep() suspends the execution of the calling thread until either at least the time specified in *req has elapsed, or the delivery of a signal that triggers the invocation of a handler in the calling thread or that terminates the process.
If the call is interrupted by a signal handler, nanosleep() returns -1, sets errno to EINTR, and writes the remaining time into the structure pointed to by rem unless rem is NULL. The value of *rem can then be used to call nanosleep() again and complete the specified pause.
If the function gets interrupted before the specified time is elapsed, the return value (ret) would be -1 and the errno would be EINTR. As we passed ts as both parameters, remaining time will be set to the ts itself. We are calling the function with remaining time (modified ts). This process will continue until the total specified time is elapsed.
Using select()
We use select() function to check whether a file descriptor is set. If we don’t pass any file descriptor, then it will time out after the specified timeout. Here also we’ll have millisecond level control.
#include <sys/time.h>
void msleep(int tms)
{
struct timeval tv;
tv.tv_sec = tms / 1000;
tv.tv_usec = (tms % 1000) * 1000;
select (0, NULL, NULL, NULL, &tv);
}
The timeval structure takes the duration in seconds and microseconds. We converted the input milliseconds into seconds and microseconds and passed that in select() function as timeout.