I'm trying to use sem_timedwait() to repeatedly lock and unlock a semaphore. Based on the example here, I was setting my struct timespec in the following way for a 20 msec timeout:
sem_t semaphore; //initialized somewhere else
void waitForSomething ()
{
int ret;
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
{
//throw error
}
ts.tv_nsec += 20000000; //timeout of 20 msec
while ((ret = sem_timedwait(&semaphore, &ts)) == -1 && errno == EINTR)
continue;
if (ret == -1 && errno != ETIMEDOUT) {
//flag error
} else {
//do something
}
return;
}
With the above code, my program would consistently fail after running for some time with the EINVAL error code. After debugging I realized that the failure was coming from the fact that ts.tv_nsec exceeds 1000000000 after some time. My current workaround is the following:
sem_t semaphore; //initialized somewhere else
void waitForSomething ()
{
int ret;
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
{
//throw error
}
if (ts.tv_nsec > 979999999) {
ts.tv_sec += 60;
ts.tv_nsec = (ts.tv_nsec + 20000000) % 1000000000;
} else {
ts.tv_nsec += 20000000;
}
while ((ret = sem_timedwait(&semaphore, &ts)) == -1 && errno == EINTR)
continue;
if (ret == -1 && errno != ETIMEDOUT) {
//throw error
} else {
// do something
}
return;
}
I'm wondering - is there a better way to do this without having to directly adjust the timespec value yourself?
As far as I know, you need to normalize a timespec
structure properly after you add an interval to tv_nsec
.
One thing you can do is:
ts.tv_nsec += 20000000;
ts.tv_sec += ts.tv_nsec / 1000000000;
ts.tv_nsec %= 1000000000;
In Linux kernel, you can use set_normalized_timespec()
to perform the normalization for you. Refer to here.